@kevinrabun/judges 3.40.0 → 3.42.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.
- package/CHANGELOG.md +39 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +133 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/auto-calibrate.d.ts +15 -0
- package/dist/commands/auto-calibrate.d.ts.map +1 -0
- package/dist/commands/auto-calibrate.js +107 -0
- package/dist/commands/auto-calibrate.js.map +1 -0
- package/dist/commands/auto-triage.d.ts +32 -0
- package/dist/commands/auto-triage.d.ts.map +1 -0
- package/dist/commands/auto-triage.js +126 -0
- package/dist/commands/auto-triage.js.map +1 -0
- package/dist/commands/config-migrate.d.ts +44 -0
- package/dist/commands/config-migrate.d.ts.map +1 -0
- package/dist/commands/config-migrate.js +241 -0
- package/dist/commands/config-migrate.js.map +1 -0
- package/dist/commands/coverage-map.d.ts +23 -0
- package/dist/commands/coverage-map.d.ts.map +1 -0
- package/dist/commands/coverage-map.js +223 -0
- package/dist/commands/coverage-map.js.map +1 -0
- package/dist/commands/dedup-report.d.ts +13 -0
- package/dist/commands/dedup-report.d.ts.map +1 -0
- package/dist/commands/dedup-report.js +138 -0
- package/dist/commands/dedup-report.js.map +1 -0
- package/dist/commands/dep-audit.d.ts +53 -0
- package/dist/commands/dep-audit.d.ts.map +1 -0
- package/dist/commands/dep-audit.js +278 -0
- package/dist/commands/dep-audit.js.map +1 -0
- package/dist/commands/deprecated.d.ts +48 -0
- package/dist/commands/deprecated.d.ts.map +1 -0
- package/dist/commands/deprecated.js +202 -0
- package/dist/commands/deprecated.js.map +1 -0
- package/dist/commands/diff-only.d.ts +34 -0
- package/dist/commands/diff-only.d.ts.map +1 -0
- package/dist/commands/diff-only.js +152 -0
- package/dist/commands/diff-only.js.map +1 -0
- package/dist/commands/fix-pr.d.ts +23 -0
- package/dist/commands/fix-pr.d.ts.map +1 -0
- package/dist/commands/fix-pr.js +323 -0
- package/dist/commands/fix-pr.js.map +1 -0
- package/dist/commands/group-findings.d.ts +23 -0
- package/dist/commands/group-findings.d.ts.map +1 -0
- package/dist/commands/group-findings.js +155 -0
- package/dist/commands/group-findings.js.map +1 -0
- package/dist/commands/interactive-fix.d.ts +23 -0
- package/dist/commands/interactive-fix.d.ts.map +1 -0
- package/dist/commands/interactive-fix.js +140 -0
- package/dist/commands/interactive-fix.js.map +1 -0
- package/dist/commands/monorepo.d.ts +38 -0
- package/dist/commands/monorepo.d.ts.map +1 -0
- package/dist/commands/monorepo.js +233 -0
- package/dist/commands/monorepo.js.map +1 -0
- package/dist/commands/notify.d.ts +79 -0
- package/dist/commands/notify.d.ts.map +1 -0
- package/dist/commands/notify.js +325 -0
- package/dist/commands/notify.js.map +1 -0
- package/dist/commands/pr-summary.d.ts +26 -0
- package/dist/commands/pr-summary.d.ts.map +1 -0
- package/dist/commands/pr-summary.js +188 -0
- package/dist/commands/pr-summary.js.map +1 -0
- package/dist/commands/profile.d.ts +38 -0
- package/dist/commands/profile.d.ts.map +1 -0
- package/dist/commands/profile.js +102 -0
- package/dist/commands/profile.js.map +1 -0
- package/dist/commands/quality-gate.d.ts +70 -0
- package/dist/commands/quality-gate.d.ts.map +1 -0
- package/dist/commands/quality-gate.js +264 -0
- package/dist/commands/quality-gate.js.map +1 -0
- package/dist/commands/smart-select.d.ts +27 -0
- package/dist/commands/smart-select.d.ts.map +1 -0
- package/dist/commands/smart-select.js +346 -0
- package/dist/commands/smart-select.js.map +1 -0
- package/dist/commands/upload.d.ts +14 -0
- package/dist/commands/upload.d.ts.map +1 -0
- package/dist/commands/upload.js +173 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/commands/validate-config.d.ts +17 -0
- package/dist/commands/validate-config.d.ts.map +1 -0
- package/dist/commands/validate-config.js +268 -0
- package/dist/commands/validate-config.js.map +1 -0
- package/dist/commands/warm-cache.d.ts +31 -0
- package/dist/commands/warm-cache.d.ts.map +1 -0
- package/dist/commands/warm-cache.js +166 -0
- package/dist/commands/warm-cache.js.map +1 -0
- package/dist/evaluators/framework-rules.d.ts +59 -0
- package/dist/evaluators/framework-rules.d.ts.map +1 -0
- package/dist/evaluators/framework-rules.js +292 -0
- package/dist/evaluators/framework-rules.js.map +1 -0
- package/dist/parallel.d.ts +53 -0
- package/dist/parallel.d.ts.map +1 -0
- package/dist/parallel.js +170 -0
- package/dist/parallel.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-aware detection module.
|
|
3
|
+
*
|
|
4
|
+
* Detects which framework(s) are in use and provides framework-specific
|
|
5
|
+
* pattern adjustments to reduce false positives. For example:
|
|
6
|
+
* - React: hooks ordering rules, JSX injection awareness
|
|
7
|
+
* - Express/Fastify: middleware chain analysis
|
|
8
|
+
* - Django: ORM injection patterns
|
|
9
|
+
* - Spring: security annotation awareness
|
|
10
|
+
* - Next.js: server-component vs client-component context
|
|
11
|
+
*
|
|
12
|
+
* This module is used by evaluators to adjust their confidence scores
|
|
13
|
+
* and disable irrelevant rules based on framework context.
|
|
14
|
+
*/
|
|
15
|
+
// ─── Framework Profiles ─────────────────────────────────────────────────────
|
|
16
|
+
const FRAMEWORK_PROFILES = [
|
|
17
|
+
{
|
|
18
|
+
id: "react",
|
|
19
|
+
name: "React",
|
|
20
|
+
languages: ["typescript", "javascript"],
|
|
21
|
+
detectPatterns: [
|
|
22
|
+
/import\s+.*\bReact\b.*from\s+['"]react['"]/,
|
|
23
|
+
/from\s+['"]react['"]/,
|
|
24
|
+
/import\s+{.*useState|useEffect|useRef|useMemo|useCallback.*}\s+from\s+['"]react['"]/,
|
|
25
|
+
],
|
|
26
|
+
fpProne: ["PERF-002", "DOC-003"],
|
|
27
|
+
severityAdjustments: [
|
|
28
|
+
{ rulePattern: "SEC-XSS", adjustment: "downgrade", reason: "React auto-escapes JSX by default" },
|
|
29
|
+
],
|
|
30
|
+
frameworkRules: [
|
|
31
|
+
{
|
|
32
|
+
id: "FW-REACT-001",
|
|
33
|
+
pattern: /useEffect\s*\(\s*(?:async|(?:\(\)\s*=>\s*\{[\s\S]*?await))/,
|
|
34
|
+
severity: "medium",
|
|
35
|
+
title: "Async function in useEffect",
|
|
36
|
+
description: "useEffect callbacks should not be async. Use an inner async function instead.",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "FW-REACT-002",
|
|
40
|
+
pattern: /dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:/,
|
|
41
|
+
severity: "high",
|
|
42
|
+
title: "dangerouslySetInnerHTML usage",
|
|
43
|
+
description: "Direct HTML injection via dangerouslySetInnerHTML — ensure content is sanitized.",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "FW-REACT-003",
|
|
47
|
+
pattern: /useEffect\s*\(\s*\(\)\s*=>\s*\{[\s\S]*?\}\s*\)\s*;/,
|
|
48
|
+
severity: "low",
|
|
49
|
+
title: "useEffect missing dependency array",
|
|
50
|
+
description: "useEffect without a dependency array runs on every render.",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "nextjs",
|
|
56
|
+
name: "Next.js",
|
|
57
|
+
languages: ["typescript", "javascript"],
|
|
58
|
+
detectPatterns: [/from\s+['"]next\//, /import\s+.*from\s+['"]next\//],
|
|
59
|
+
fpProne: ["PERF-002"],
|
|
60
|
+
severityAdjustments: [
|
|
61
|
+
{
|
|
62
|
+
rulePattern: "SEC-XSS",
|
|
63
|
+
adjustment: "downgrade",
|
|
64
|
+
reason: "Next.js uses React auto-escaping and server components",
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
frameworkRules: [
|
|
68
|
+
{
|
|
69
|
+
id: "FW-NEXT-001",
|
|
70
|
+
pattern: /getServerSideProps.*\bres\.end\b/s,
|
|
71
|
+
severity: "medium",
|
|
72
|
+
title: "Response ended in getServerSideProps",
|
|
73
|
+
description: "Calling res.end() in getServerSideProps can cause unexpected behavior.",
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: "express",
|
|
79
|
+
name: "Express.js",
|
|
80
|
+
languages: ["typescript", "javascript"],
|
|
81
|
+
detectPatterns: [/from\s+['"]express['"]/, /require\s*\(\s*['"]express['"]\s*\)/],
|
|
82
|
+
fpProne: [],
|
|
83
|
+
severityAdjustments: [],
|
|
84
|
+
frameworkRules: [
|
|
85
|
+
{
|
|
86
|
+
id: "FW-EXPRESS-001",
|
|
87
|
+
pattern: /app\.(get|post|put|delete|patch)\s*\([^,]+,\s*(?:async\s+)?\(\s*req\s*,\s*res\s*\)\s*=>\s*\{(?:(?!try\s*\{)[\s\S])*\}\s*\)/,
|
|
88
|
+
severity: "medium",
|
|
89
|
+
title: "Express route handler without error handling",
|
|
90
|
+
description: "Route handler lacks try-catch — unhandled errors crash the process.",
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: "FW-EXPRESS-002",
|
|
94
|
+
pattern: /app\.use\s*\(\s*cors\s*\(\s*\)\s*\)/,
|
|
95
|
+
severity: "high",
|
|
96
|
+
title: "CORS with no origin restriction",
|
|
97
|
+
description: "cors() with no options allows all origins. Restrict to trusted domains.",
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "fastify",
|
|
103
|
+
name: "Fastify",
|
|
104
|
+
languages: ["typescript", "javascript"],
|
|
105
|
+
detectPatterns: [/from\s+['"]fastify['"]/, /require\s*\(\s*['"]fastify['"]\s*\)/],
|
|
106
|
+
fpProne: [],
|
|
107
|
+
severityAdjustments: [
|
|
108
|
+
{ rulePattern: "PERF-", adjustment: "downgrade", reason: "Fastify has built-in perf optimizations" },
|
|
109
|
+
],
|
|
110
|
+
frameworkRules: [],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: "django",
|
|
114
|
+
name: "Django",
|
|
115
|
+
languages: ["python"],
|
|
116
|
+
detectPatterns: [/from\s+django/, /import\s+django/],
|
|
117
|
+
fpProne: ["SEC-SQLI-001"],
|
|
118
|
+
severityAdjustments: [
|
|
119
|
+
{ rulePattern: "SEC-SQLI", adjustment: "downgrade", reason: "Django ORM auto-parameterizes queries" },
|
|
120
|
+
],
|
|
121
|
+
frameworkRules: [
|
|
122
|
+
{
|
|
123
|
+
id: "FW-DJANGO-001",
|
|
124
|
+
pattern: /\.raw\s*\(\s*f['"]|\.raw\s*\(\s*['"].*%s/,
|
|
125
|
+
severity: "critical",
|
|
126
|
+
title: "Raw SQL with user input in Django",
|
|
127
|
+
description: "Using .raw() with f-strings or %s formatting bypasses Django ORM protection.",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: "FW-DJANGO-002",
|
|
131
|
+
pattern: /\|safe\b/,
|
|
132
|
+
severity: "high",
|
|
133
|
+
title: "Django template |safe filter",
|
|
134
|
+
description: "The |safe filter disables auto-escaping — ensure content is trusted.",
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: "flask",
|
|
140
|
+
name: "Flask",
|
|
141
|
+
languages: ["python"],
|
|
142
|
+
detectPatterns: [/from\s+flask\s+import/, /import\s+flask/],
|
|
143
|
+
fpProne: [],
|
|
144
|
+
severityAdjustments: [],
|
|
145
|
+
frameworkRules: [
|
|
146
|
+
{
|
|
147
|
+
id: "FW-FLASK-001",
|
|
148
|
+
pattern: /app\.run\s*\(\s*debug\s*=\s*True/,
|
|
149
|
+
severity: "high",
|
|
150
|
+
title: "Flask debug mode in production",
|
|
151
|
+
description: "Debug mode exposes the Werkzeug debugger — never enable in production.",
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
id: "fastapi",
|
|
157
|
+
name: "FastAPI",
|
|
158
|
+
languages: ["python"],
|
|
159
|
+
detectPatterns: [/from\s+fastapi\s+import/, /import\s+fastapi/],
|
|
160
|
+
fpProne: ["SEC-SQLI-001"],
|
|
161
|
+
severityAdjustments: [
|
|
162
|
+
{ rulePattern: "PERF-", adjustment: "downgrade", reason: "FastAPI has built-in async performance" },
|
|
163
|
+
],
|
|
164
|
+
frameworkRules: [],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: "spring",
|
|
168
|
+
name: "Spring Boot",
|
|
169
|
+
languages: ["java"],
|
|
170
|
+
detectPatterns: [/@SpringBootApplication/, /import\s+org\.springframework\./],
|
|
171
|
+
fpProne: ["AUTH-001"],
|
|
172
|
+
severityAdjustments: [
|
|
173
|
+
{
|
|
174
|
+
rulePattern: "AUTH-",
|
|
175
|
+
adjustment: "downgrade",
|
|
176
|
+
reason: "Spring Security annotation-based auth is often not visible in single-file analysis",
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
frameworkRules: [
|
|
180
|
+
{
|
|
181
|
+
id: "FW-SPRING-001",
|
|
182
|
+
pattern: /@RequestMapping\s*(?!\(.*method\s*=)/,
|
|
183
|
+
severity: "medium",
|
|
184
|
+
title: "RequestMapping without HTTP method",
|
|
185
|
+
description: "@RequestMapping without explicit method accepts all HTTP methods.",
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: "rails",
|
|
191
|
+
name: "Ruby on Rails",
|
|
192
|
+
languages: ["ruby"],
|
|
193
|
+
detectPatterns: [/class\s+\w+\s*<\s*ApplicationController/, /Rails\.application/],
|
|
194
|
+
fpProne: ["SEC-XSS"],
|
|
195
|
+
severityAdjustments: [
|
|
196
|
+
{ rulePattern: "SEC-XSS", adjustment: "downgrade", reason: "Rails auto-escapes ERB output by default" },
|
|
197
|
+
{ rulePattern: "SEC-SQLI", adjustment: "downgrade", reason: "ActiveRecord auto-parameterizes queries" },
|
|
198
|
+
],
|
|
199
|
+
frameworkRules: [
|
|
200
|
+
{
|
|
201
|
+
id: "FW-RAILS-001",
|
|
202
|
+
pattern: /\.html_safe\b/,
|
|
203
|
+
severity: "high",
|
|
204
|
+
title: "html_safe bypasses Rails escaping",
|
|
205
|
+
description: "html_safe marks a string as safe — ensure content is sanitized first.",
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
},
|
|
209
|
+
];
|
|
210
|
+
// ─── Detection ──────────────────────────────────────────────────────────────
|
|
211
|
+
/**
|
|
212
|
+
* Detect which frameworks are present in the given code.
|
|
213
|
+
*/
|
|
214
|
+
export function detectFrameworks(code, language) {
|
|
215
|
+
return FRAMEWORK_PROFILES.filter((fw) => {
|
|
216
|
+
if (!fw.languages.includes(language))
|
|
217
|
+
return false;
|
|
218
|
+
return fw.detectPatterns.some((p) => p.test(code));
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
// ─── Finding Adjustment ─────────────────────────────────────────────────────
|
|
222
|
+
const SEVERITY_RANK = { critical: 4, high: 3, medium: 2, low: 1, info: 0 };
|
|
223
|
+
const RANK_TO_SEVERITY = { 4: "critical", 3: "high", 2: "medium", 1: "low", 0: "info" };
|
|
224
|
+
/**
|
|
225
|
+
* Adjust findings based on detected framework context.
|
|
226
|
+
* - Downgrades severity for FP-prone rules in the framework
|
|
227
|
+
* - Applies framework-specific severity adjustments
|
|
228
|
+
* - Tags adjusted findings with provenance
|
|
229
|
+
*/
|
|
230
|
+
export function adjustFindingsForFramework(findings, frameworks) {
|
|
231
|
+
if (frameworks.length === 0)
|
|
232
|
+
return findings;
|
|
233
|
+
const fpProne = new Set(frameworks.flatMap((fw) => fw.fpProne));
|
|
234
|
+
const adjustments = frameworks.flatMap((fw) => fw.severityAdjustments);
|
|
235
|
+
return findings.map((f) => {
|
|
236
|
+
let adjusted = { ...f };
|
|
237
|
+
// Downgrade FP-prone rules
|
|
238
|
+
if (fpProne.has(f.ruleId)) {
|
|
239
|
+
const rank = SEVERITY_RANK[f.severity] ?? 2;
|
|
240
|
+
const newRank = Math.max(0, rank - 1);
|
|
241
|
+
adjusted = {
|
|
242
|
+
...adjusted,
|
|
243
|
+
severity: RANK_TO_SEVERITY[newRank] ?? f.severity,
|
|
244
|
+
confidence: Math.max(0, (f.confidence ?? 0.5) - 0.15),
|
|
245
|
+
evidenceBasis: `${f.evidenceBasis ?? ""} framework-adjusted(-0.15)`.trim(),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
// Apply severity adjustments
|
|
249
|
+
for (const adj of adjustments) {
|
|
250
|
+
if (f.ruleId.startsWith(adj.rulePattern) || f.ruleId.includes(adj.rulePattern)) {
|
|
251
|
+
const rank = SEVERITY_RANK[adjusted.severity] ?? 2;
|
|
252
|
+
const delta = adj.adjustment === "downgrade" ? -1 : 1;
|
|
253
|
+
const newRank = Math.max(0, Math.min(4, rank + delta));
|
|
254
|
+
adjusted = {
|
|
255
|
+
...adjusted,
|
|
256
|
+
severity: RANK_TO_SEVERITY[newRank] ?? adjusted.severity,
|
|
257
|
+
evidenceBasis: `${adjusted.evidenceBasis ?? ""} ${adj.reason}`.trim(),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return adjusted;
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
// ─── Framework-Specific Rule Evaluation ─────────────────────────────────────
|
|
265
|
+
/**
|
|
266
|
+
* Run framework-specific rules against code.
|
|
267
|
+
* Returns additional findings from framework-aware patterns.
|
|
268
|
+
*/
|
|
269
|
+
export function evaluateFrameworkRules(code, language) {
|
|
270
|
+
const frameworks = detectFrameworks(code, language);
|
|
271
|
+
const findings = [];
|
|
272
|
+
for (const fw of frameworks) {
|
|
273
|
+
for (const rule of fw.frameworkRules) {
|
|
274
|
+
const matches = code.matchAll(new RegExp(rule.pattern, "g"));
|
|
275
|
+
for (const match of matches) {
|
|
276
|
+
const lineNumber = code.substring(0, match.index ?? 0).split("\n").length;
|
|
277
|
+
findings.push({
|
|
278
|
+
ruleId: rule.id,
|
|
279
|
+
severity: rule.severity,
|
|
280
|
+
title: `[${fw.name}] ${rule.title}`,
|
|
281
|
+
description: rule.description,
|
|
282
|
+
lineNumbers: [lineNumber],
|
|
283
|
+
recommendation: rule.description,
|
|
284
|
+
confidence: 0.85,
|
|
285
|
+
provenance: "framework-knowledge",
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return findings;
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=framework-rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"framework-rules.js","sourceRoot":"","sources":["../../src/evaluators/framework-rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parallel file processing for large repositories.
|
|
3
|
+
*
|
|
4
|
+
* Uses worker_threads to evaluate multiple files concurrently,
|
|
5
|
+
* providing significant speedup for multi-file evaluations.
|
|
6
|
+
*
|
|
7
|
+
* This module provides a pool-based parallelization strategy:
|
|
8
|
+
* - Creates a fixed pool of workers (defaults to available CPUs - 1)
|
|
9
|
+
* - Distributes files across workers for concurrent evaluation
|
|
10
|
+
* - Collects and merges results back on the main thread
|
|
11
|
+
*/
|
|
12
|
+
import type { TribunalVerdict } from "./types.js";
|
|
13
|
+
export interface ParallelEvalTask {
|
|
14
|
+
filePath: string;
|
|
15
|
+
language: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ParallelEvalResult {
|
|
18
|
+
filePath: string;
|
|
19
|
+
language: string;
|
|
20
|
+
verdict: TribunalVerdict;
|
|
21
|
+
durationMs: number;
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ParallelEvalSummary {
|
|
25
|
+
results: ParallelEvalResult[];
|
|
26
|
+
totalFiles: number;
|
|
27
|
+
totalFindings: number;
|
|
28
|
+
totalDurationMs: number;
|
|
29
|
+
workerCount: number;
|
|
30
|
+
}
|
|
31
|
+
export declare function detectLanguage(filePath: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Sequential evaluation — used as fallback or for small file counts.
|
|
34
|
+
*/
|
|
35
|
+
export declare function evaluateSequential(files: ParallelEvalTask[], evaluator: (code: string, language: string) => TribunalVerdict): ParallelEvalSummary;
|
|
36
|
+
/**
|
|
37
|
+
* Evaluate files in parallel using a concurrency-limited promise pool.
|
|
38
|
+
* Uses a simple async pool pattern without worker_threads for simplicity
|
|
39
|
+
* and compatibility (evaluators must be imported in the same context).
|
|
40
|
+
*
|
|
41
|
+
* @param files - Files to evaluate
|
|
42
|
+
* @param evaluator - The evaluation function to call per file
|
|
43
|
+
* @param concurrency - Maximum concurrent evaluations (default: CPU count - 1)
|
|
44
|
+
*/
|
|
45
|
+
export declare function evaluateParallel(files: ParallelEvalTask[], evaluator: (code: string, language: string) => TribunalVerdict, concurrency?: number): Promise<ParallelEvalSummary>;
|
|
46
|
+
/**
|
|
47
|
+
* Convenience function: given file paths, detect languages and evaluate
|
|
48
|
+
* in parallel. Returns sorted results (failures first).
|
|
49
|
+
*/
|
|
50
|
+
export declare function batchEvaluate(filePaths: string[], evaluator: (code: string, language: string) => TribunalVerdict, options?: {
|
|
51
|
+
concurrency?: number;
|
|
52
|
+
}): Promise<ParallelEvalSummary>;
|
|
53
|
+
//# sourceMappingURL=parallel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parallel.d.ts","sourceRoot":"","sources":["../src/parallel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAIlD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAiBD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;AAID;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,gBAAgB,EAAE,EACzB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,eAAe,GAC7D,mBAAmB,CA0CrB;AAID;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,gBAAgB,EAAE,EACzB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,eAAe,EAC9D,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,mBAAmB,CAAC,CAoE9B;AAID;;;GAGG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EAAE,EACnB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,eAAe,EAC9D,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACjC,OAAO,CAAC,mBAAmB,CAAC,CAgB9B"}
|
package/dist/parallel.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parallel file processing for large repositories.
|
|
3
|
+
*
|
|
4
|
+
* Uses worker_threads to evaluate multiple files concurrently,
|
|
5
|
+
* providing significant speedup for multi-file evaluations.
|
|
6
|
+
*
|
|
7
|
+
* This module provides a pool-based parallelization strategy:
|
|
8
|
+
* - Creates a fixed pool of workers (defaults to available CPUs - 1)
|
|
9
|
+
* - Distributes files across workers for concurrent evaluation
|
|
10
|
+
* - Collects and merges results back on the main thread
|
|
11
|
+
*/
|
|
12
|
+
import { cpus } from "os";
|
|
13
|
+
import { readFileSync } from "fs";
|
|
14
|
+
import { extname } from "path";
|
|
15
|
+
// ─── Language Detection ─────────────────────────────────────────────────────
|
|
16
|
+
const EXT_TO_LANG = {
|
|
17
|
+
".ts": "typescript",
|
|
18
|
+
".tsx": "typescript",
|
|
19
|
+
".js": "javascript",
|
|
20
|
+
".jsx": "javascript",
|
|
21
|
+
".py": "python",
|
|
22
|
+
".rs": "rust",
|
|
23
|
+
".go": "go",
|
|
24
|
+
".java": "java",
|
|
25
|
+
".cs": "csharp",
|
|
26
|
+
".cpp": "cpp",
|
|
27
|
+
};
|
|
28
|
+
export function detectLanguage(filePath) {
|
|
29
|
+
return EXT_TO_LANG[extname(filePath).toLowerCase()] || "typescript";
|
|
30
|
+
}
|
|
31
|
+
// ─── Sequential (fallback) ──────────────────────────────────────────────────
|
|
32
|
+
/**
|
|
33
|
+
* Sequential evaluation — used as fallback or for small file counts.
|
|
34
|
+
*/
|
|
35
|
+
export function evaluateSequential(files, evaluator) {
|
|
36
|
+
const start = Date.now();
|
|
37
|
+
const results = [];
|
|
38
|
+
for (const task of files) {
|
|
39
|
+
const taskStart = Date.now();
|
|
40
|
+
try {
|
|
41
|
+
const code = readFileSync(task.filePath, "utf-8");
|
|
42
|
+
const verdict = evaluator(code, task.language);
|
|
43
|
+
results.push({
|
|
44
|
+
filePath: task.filePath,
|
|
45
|
+
language: task.language,
|
|
46
|
+
verdict,
|
|
47
|
+
durationMs: Date.now() - taskStart,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
results.push({
|
|
52
|
+
filePath: task.filePath,
|
|
53
|
+
language: task.language,
|
|
54
|
+
verdict: {
|
|
55
|
+
overallVerdict: "fail",
|
|
56
|
+
overallScore: 0,
|
|
57
|
+
summary: `Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
58
|
+
evaluations: [],
|
|
59
|
+
findings: [],
|
|
60
|
+
criticalCount: 0,
|
|
61
|
+
highCount: 0,
|
|
62
|
+
timestamp: new Date().toISOString(),
|
|
63
|
+
},
|
|
64
|
+
durationMs: Date.now() - taskStart,
|
|
65
|
+
error: err instanceof Error ? err.message : String(err),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
results,
|
|
71
|
+
totalFiles: files.length,
|
|
72
|
+
totalFindings: results.reduce((sum, r) => sum + r.verdict.findings.length, 0),
|
|
73
|
+
totalDurationMs: Date.now() - start,
|
|
74
|
+
workerCount: 1,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// ─── Parallel Evaluation via Promise Pool ───────────────────────────────────
|
|
78
|
+
/**
|
|
79
|
+
* Evaluate files in parallel using a concurrency-limited promise pool.
|
|
80
|
+
* Uses a simple async pool pattern without worker_threads for simplicity
|
|
81
|
+
* and compatibility (evaluators must be imported in the same context).
|
|
82
|
+
*
|
|
83
|
+
* @param files - Files to evaluate
|
|
84
|
+
* @param evaluator - The evaluation function to call per file
|
|
85
|
+
* @param concurrency - Maximum concurrent evaluations (default: CPU count - 1)
|
|
86
|
+
*/
|
|
87
|
+
export async function evaluateParallel(files, evaluator, concurrency) {
|
|
88
|
+
const maxConcurrency = concurrency ?? Math.max(1, cpus().length - 1);
|
|
89
|
+
const start = Date.now();
|
|
90
|
+
const results = [];
|
|
91
|
+
// For very small file counts, use sequential
|
|
92
|
+
if (files.length <= 2) {
|
|
93
|
+
return evaluateSequential(files, evaluator);
|
|
94
|
+
}
|
|
95
|
+
// Promise pool implementation
|
|
96
|
+
let index = 0;
|
|
97
|
+
const workers = [];
|
|
98
|
+
async function processNext() {
|
|
99
|
+
while (index < files.length) {
|
|
100
|
+
const currentIndex = index++;
|
|
101
|
+
const task = files[currentIndex];
|
|
102
|
+
const taskStart = Date.now();
|
|
103
|
+
try {
|
|
104
|
+
const code = readFileSync(task.filePath, "utf-8");
|
|
105
|
+
const verdict = evaluator(code, task.language);
|
|
106
|
+
results.push({
|
|
107
|
+
filePath: task.filePath,
|
|
108
|
+
language: task.language,
|
|
109
|
+
verdict,
|
|
110
|
+
durationMs: Date.now() - taskStart,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
results.push({
|
|
115
|
+
filePath: task.filePath,
|
|
116
|
+
language: task.language,
|
|
117
|
+
verdict: {
|
|
118
|
+
overallVerdict: "fail",
|
|
119
|
+
overallScore: 0,
|
|
120
|
+
summary: `Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
121
|
+
evaluations: [],
|
|
122
|
+
findings: [],
|
|
123
|
+
criticalCount: 0,
|
|
124
|
+
highCount: 0,
|
|
125
|
+
timestamp: new Date().toISOString(),
|
|
126
|
+
},
|
|
127
|
+
durationMs: Date.now() - taskStart,
|
|
128
|
+
error: err instanceof Error ? err.message : String(err),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// Yield to allow other work to proceed
|
|
132
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Launch pool
|
|
136
|
+
const poolSize = Math.min(maxConcurrency, files.length);
|
|
137
|
+
for (let i = 0; i < poolSize; i++) {
|
|
138
|
+
workers.push(processNext());
|
|
139
|
+
}
|
|
140
|
+
await Promise.all(workers);
|
|
141
|
+
return {
|
|
142
|
+
results,
|
|
143
|
+
totalFiles: files.length,
|
|
144
|
+
totalFindings: results.reduce((sum, r) => sum + r.verdict.findings.length, 0),
|
|
145
|
+
totalDurationMs: Date.now() - start,
|
|
146
|
+
workerCount: poolSize,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
// ─── Batch Evaluation Helper ────────────────────────────────────────────────
|
|
150
|
+
/**
|
|
151
|
+
* Convenience function: given file paths, detect languages and evaluate
|
|
152
|
+
* in parallel. Returns sorted results (failures first).
|
|
153
|
+
*/
|
|
154
|
+
export async function batchEvaluate(filePaths, evaluator, options) {
|
|
155
|
+
const tasks = filePaths.map((fp) => ({
|
|
156
|
+
filePath: fp,
|
|
157
|
+
language: detectLanguage(fp),
|
|
158
|
+
}));
|
|
159
|
+
const summary = await evaluateParallel(tasks, evaluator, options?.concurrency);
|
|
160
|
+
// Sort: failures first, then by finding count descending
|
|
161
|
+
summary.results.sort((a, b) => {
|
|
162
|
+
if (a.verdict.overallVerdict === "fail" && b.verdict.overallVerdict !== "fail")
|
|
163
|
+
return -1;
|
|
164
|
+
if (b.verdict.overallVerdict === "fail" && a.verdict.overallVerdict !== "fail")
|
|
165
|
+
return 1;
|
|
166
|
+
return b.verdict.findings.length - a.verdict.findings.length;
|
|
167
|
+
});
|
|
168
|
+
return summary;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=parallel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parallel.js","sourceRoot":"","sources":["../src/parallel.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/kevinrabun/judges",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "3.
|
|
10
|
+
"version": "3.42.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@kevinrabun/judges",
|
|
15
|
-
"version": "3.
|
|
15
|
+
"version": "3.42.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|