@eddacraft/anvil-runtime 0.1.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/LICENSE +14 -0
- package/dist/cache/cache-key.d.ts +45 -0
- package/dist/cache/cache-key.d.ts.map +1 -0
- package/dist/cache/cache-key.js +135 -0
- package/dist/cache/index.d.ts +27 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +38 -0
- package/dist/cache/providers/file-cache.d.ts +63 -0
- package/dist/cache/providers/file-cache.d.ts.map +1 -0
- package/dist/cache/providers/file-cache.js +369 -0
- package/dist/cache/providers/memory-cache.d.ts +52 -0
- package/dist/cache/providers/memory-cache.d.ts.map +1 -0
- package/dist/cache/providers/memory-cache.js +197 -0
- package/dist/cache/providers/null-cache.d.ts +26 -0
- package/dist/cache/providers/null-cache.d.ts.map +1 -0
- package/dist/cache/providers/null-cache.js +50 -0
- package/dist/cache/types.d.ts +114 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +4 -0
- package/dist/concurrency/agent.d.ts +137 -0
- package/dist/concurrency/agent.d.ts.map +1 -0
- package/dist/concurrency/agent.js +440 -0
- package/dist/concurrency/atomic.d.ts +93 -0
- package/dist/concurrency/atomic.d.ts.map +1 -0
- package/dist/concurrency/atomic.js +281 -0
- package/dist/concurrency/git-agent.d.ts +114 -0
- package/dist/concurrency/git-agent.d.ts.map +1 -0
- package/dist/concurrency/git-agent.js +313 -0
- package/dist/concurrency/index.d.ts +95 -0
- package/dist/concurrency/index.d.ts.map +1 -0
- package/dist/concurrency/index.js +127 -0
- package/dist/concurrency/lock-manager.d.ts +170 -0
- package/dist/concurrency/lock-manager.d.ts.map +1 -0
- package/dist/concurrency/lock-manager.js +525 -0
- package/dist/concurrency/queue-manager.d.ts +166 -0
- package/dist/concurrency/queue-manager.d.ts.map +1 -0
- package/dist/concurrency/queue-manager.js +442 -0
- package/dist/concurrency/types.d.ts +382 -0
- package/dist/concurrency/types.d.ts.map +1 -0
- package/dist/concurrency/types.js +204 -0
- package/dist/export/constraint-collector.d.ts +175 -0
- package/dist/export/constraint-collector.d.ts.map +1 -0
- package/dist/export/constraint-collector.js +203 -0
- package/dist/export/formatters/llms-txt-formatter.d.ts +89 -0
- package/dist/export/formatters/llms-txt-formatter.d.ts.map +1 -0
- package/dist/export/formatters/llms-txt-formatter.js +249 -0
- package/dist/export/formatters/mcp-resource-formatter.d.ts +186 -0
- package/dist/export/formatters/mcp-resource-formatter.d.ts.map +1 -0
- package/dist/export/formatters/mcp-resource-formatter.js +139 -0
- package/dist/export/formatters/prompt-formatter.d.ts +83 -0
- package/dist/export/formatters/prompt-formatter.d.ts.map +1 -0
- package/dist/export/formatters/prompt-formatter.js +256 -0
- package/dist/export/index.d.ts +10 -0
- package/dist/export/index.d.ts.map +1 -0
- package/dist/export/index.js +9 -0
- package/dist/gate/check.interface.d.ts +15 -0
- package/dist/gate/check.interface.d.ts.map +1 -0
- package/dist/gate/check.interface.js +18 -0
- package/dist/gate/checks/antipattern.check.d.ts +27 -0
- package/dist/gate/checks/antipattern.check.d.ts.map +1 -0
- package/dist/gate/checks/antipattern.check.js +140 -0
- package/dist/gate/checks/architecture/circular-detector.d.ts +33 -0
- package/dist/gate/checks/architecture/circular-detector.d.ts.map +1 -0
- package/dist/gate/checks/architecture/circular-detector.js +71 -0
- package/dist/gate/checks/architecture/dependency-analyzer.d.ts +81 -0
- package/dist/gate/checks/architecture/dependency-analyzer.d.ts.map +1 -0
- package/dist/gate/checks/architecture/dependency-analyzer.js +136 -0
- package/dist/gate/checks/architecture/layer-validator.d.ts +75 -0
- package/dist/gate/checks/architecture/layer-validator.d.ts.map +1 -0
- package/dist/gate/checks/architecture/layer-validator.js +193 -0
- package/dist/gate/checks/architecture.check.d.ts +56 -0
- package/dist/gate/checks/architecture.check.d.ts.map +1 -0
- package/dist/gate/checks/architecture.check.js +394 -0
- package/dist/gate/checks/command-safety.check.d.ts +12 -0
- package/dist/gate/checks/command-safety.check.d.ts.map +1 -0
- package/dist/gate/checks/command-safety.check.js +230 -0
- package/dist/gate/checks/coverage.check.d.ts +9 -0
- package/dist/gate/checks/coverage.check.d.ts.map +1 -0
- package/dist/gate/checks/coverage.check.js +81 -0
- package/dist/gate/checks/dependency.check.d.ts +17 -0
- package/dist/gate/checks/dependency.check.d.ts.map +1 -0
- package/dist/gate/checks/dependency.check.js +342 -0
- package/dist/gate/checks/eslint.check.d.ts +14 -0
- package/dist/gate/checks/eslint.check.d.ts.map +1 -0
- package/dist/gate/checks/eslint.check.js +79 -0
- package/dist/gate/checks/policy.check.d.ts +78 -0
- package/dist/gate/checks/policy.check.d.ts.map +1 -0
- package/dist/gate/checks/policy.check.js +457 -0
- package/dist/gate/checks/secret/entropy-detector.d.ts +44 -0
- package/dist/gate/checks/secret/entropy-detector.d.ts.map +1 -0
- package/dist/gate/checks/secret/entropy-detector.js +76 -0
- package/dist/gate/checks/secret/git-scanner.d.ts +36 -0
- package/dist/gate/checks/secret/git-scanner.d.ts.map +1 -0
- package/dist/gate/checks/secret/git-scanner.js +90 -0
- package/dist/gate/checks/secret/secret-patterns.d.ts +42 -0
- package/dist/gate/checks/secret/secret-patterns.d.ts.map +1 -0
- package/dist/gate/checks/secret/secret-patterns.js +137 -0
- package/dist/gate/checks/secret.check.d.ts +56 -0
- package/dist/gate/checks/secret.check.d.ts.map +1 -0
- package/dist/gate/checks/secret.check.js +245 -0
- package/dist/gate/config/command-safety-config.d.ts +5 -0
- package/dist/gate/config/command-safety-config.d.ts.map +1 -0
- package/dist/gate/config/command-safety-config.js +69 -0
- package/dist/gate/config/index.d.ts +2 -0
- package/dist/gate/config/index.d.ts.map +1 -0
- package/dist/gate/config/index.js +1 -0
- package/dist/gate/formatters/command-safety-formatter.d.ts +10 -0
- package/dist/gate/formatters/command-safety-formatter.d.ts.map +1 -0
- package/dist/gate/formatters/command-safety-formatter.js +64 -0
- package/dist/gate/formatters/index.d.ts +2 -0
- package/dist/gate/formatters/index.d.ts.map +1 -0
- package/dist/gate/formatters/index.js +1 -0
- package/dist/gate/gate-config.d.ts +44 -0
- package/dist/gate/gate-config.d.ts.map +1 -0
- package/dist/gate/gate-config.js +334 -0
- package/dist/gate/gate-runner.d.ts +160 -0
- package/dist/gate/gate-runner.d.ts.map +1 -0
- package/dist/gate/gate-runner.js +531 -0
- package/dist/gate/index.d.ts +20 -0
- package/dist/gate/index.d.ts.map +1 -0
- package/dist/gate/index.js +14 -0
- package/dist/gate/parsers/command-parser.d.ts +18 -0
- package/dist/gate/parsers/command-parser.d.ts.map +1 -0
- package/dist/gate/parsers/command-parser.js +363 -0
- package/dist/gate/parsers/index.d.ts +2 -0
- package/dist/gate/parsers/index.d.ts.map +1 -0
- package/dist/gate/parsers/index.js +1 -0
- package/dist/gate/policy/index.d.ts +12 -0
- package/dist/gate/policy/index.d.ts.map +1 -0
- package/dist/gate/policy/index.js +10 -0
- package/dist/gate/rules/default-filesystem-rules.d.ts +3 -0
- package/dist/gate/rules/default-filesystem-rules.d.ts.map +1 -0
- package/dist/gate/rules/default-filesystem-rules.js +201 -0
- package/dist/gate/rules/default-git-rules.d.ts +3 -0
- package/dist/gate/rules/default-git-rules.d.ts.map +1 -0
- package/dist/gate/rules/default-git-rules.js +192 -0
- package/dist/gate/rules/index.d.ts +5 -0
- package/dist/gate/rules/index.d.ts.map +1 -0
- package/dist/gate/rules/index.js +3 -0
- package/dist/gate/rules/rule-matcher.d.ts +27 -0
- package/dist/gate/rules/rule-matcher.d.ts.map +1 -0
- package/dist/gate/rules/rule-matcher.js +228 -0
- package/dist/gate/rules/types.d.ts +250 -0
- package/dist/gate/rules/types.d.ts.map +1 -0
- package/dist/gate/rules/types.js +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/types/gate.types.d.ts +42 -0
- package/dist/types/gate.types.d.ts.map +1 -0
- package/dist/types/gate.types.js +94 -0
- package/dist/watch/debouncer.d.ts +90 -0
- package/dist/watch/debouncer.d.ts.map +1 -0
- package/dist/watch/debouncer.js +135 -0
- package/dist/watch/file-watcher.d.ts +73 -0
- package/dist/watch/file-watcher.d.ts.map +1 -0
- package/dist/watch/file-watcher.js +121 -0
- package/dist/watch/git-status.d.ts +98 -0
- package/dist/watch/git-status.d.ts.map +1 -0
- package/dist/watch/git-status.js +266 -0
- package/dist/watch/index.d.ts +16 -0
- package/dist/watch/index.d.ts.map +1 -0
- package/dist/watch/index.js +15 -0
- package/dist/watch/orchestrator.d.ts +113 -0
- package/dist/watch/orchestrator.d.ts.map +1 -0
- package/dist/watch/orchestrator.js +409 -0
- package/dist/watch/types.d.ts +190 -0
- package/dist/watch/types.d.ts.map +1 -0
- package/dist/watch/types.js +76 -0
- package/package.json +60 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { BaseCheck } from '../check.interface.js';
|
|
2
|
+
import { createDebugger } from '@eddacraft/anvil-core';
|
|
3
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
const log = createDebugger('check');
|
|
6
|
+
export class CoverageCheck extends BaseCheck {
|
|
7
|
+
name = 'coverage';
|
|
8
|
+
description = 'Check test coverage thresholds';
|
|
9
|
+
async run(context) {
|
|
10
|
+
log(`coverage check starting, workspace=${context.workspace_root}`);
|
|
11
|
+
try {
|
|
12
|
+
const coveragePath = join(context.workspace_root, 'coverage', 'coverage-summary.json');
|
|
13
|
+
if (!existsSync(coveragePath)) {
|
|
14
|
+
log(`coverage check: coverage report not found at ${coveragePath}`);
|
|
15
|
+
return this.createFailure('Coverage report not found', 'Run tests with coverage first');
|
|
16
|
+
}
|
|
17
|
+
const coverageData = JSON.parse(readFileSync(coveragePath, 'utf-8'));
|
|
18
|
+
const defaultThresholds = {
|
|
19
|
+
lines: 80,
|
|
20
|
+
functions: 80,
|
|
21
|
+
branches: 80,
|
|
22
|
+
statements: 80,
|
|
23
|
+
};
|
|
24
|
+
const thresholds = typeof context.check_config.thresholds === 'object' &&
|
|
25
|
+
context.check_config.thresholds !== null
|
|
26
|
+
? context.check_config.thresholds
|
|
27
|
+
: defaultThresholds;
|
|
28
|
+
log('coverage check: thresholds', { thresholds });
|
|
29
|
+
const results = this.analyzeCoverage(coverageData, thresholds);
|
|
30
|
+
const overallScore = results.overall;
|
|
31
|
+
const minScore = typeof context.check_config.min_score === 'number' ? context.check_config.min_score : 80;
|
|
32
|
+
const passed = overallScore >= minScore;
|
|
33
|
+
log('coverage check result', {
|
|
34
|
+
passed,
|
|
35
|
+
overall: overallScore,
|
|
36
|
+
minScore,
|
|
37
|
+
failedChecks: results.failed_checks,
|
|
38
|
+
});
|
|
39
|
+
const message = passed
|
|
40
|
+
? `Coverage passed: ${overallScore.toFixed(1)}% overall`
|
|
41
|
+
: `Coverage failed: ${overallScore.toFixed(1)}% overall (required: ${minScore}%)`;
|
|
42
|
+
return this.createResult(passed, message, overallScore, results);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
log(`coverage check error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
46
|
+
return this.createFailure('Coverage check failed', error instanceof Error ? error.message : 'Unknown error');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
analyzeCoverage(coverageData, thresholds) {
|
|
50
|
+
const totals = coverageData.total;
|
|
51
|
+
const results = {
|
|
52
|
+
overall: 0,
|
|
53
|
+
details: {},
|
|
54
|
+
passed: true,
|
|
55
|
+
failed_checks: [],
|
|
56
|
+
};
|
|
57
|
+
// Calculate overall coverage as average of all metrics
|
|
58
|
+
const metrics = ['lines', 'functions', 'branches', 'statements'];
|
|
59
|
+
let totalCoverage = 0;
|
|
60
|
+
let validMetrics = 0;
|
|
61
|
+
for (const metric of metrics) {
|
|
62
|
+
if (totals[metric]) {
|
|
63
|
+
const coverage = totals[metric].pct;
|
|
64
|
+
const threshold = thresholds[metric] || 0;
|
|
65
|
+
results.details[metric] = {
|
|
66
|
+
coverage,
|
|
67
|
+
threshold,
|
|
68
|
+
passed: coverage >= threshold,
|
|
69
|
+
};
|
|
70
|
+
totalCoverage += coverage;
|
|
71
|
+
validMetrics++;
|
|
72
|
+
if (coverage < threshold) {
|
|
73
|
+
results.passed = false;
|
|
74
|
+
results.failed_checks.push(`${metric}: ${coverage}% < ${threshold}%`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
results.overall = validMetrics > 0 ? totalCoverage / validMetrics : 0;
|
|
79
|
+
return results;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BaseCheck } from '../check.interface.js';
|
|
2
|
+
import { CheckContext, GateResult } from '../../types/gate.types.js';
|
|
3
|
+
export declare class DependencyCheck extends BaseCheck {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
run(context: CheckContext): Promise<GateResult>;
|
|
7
|
+
private detectPackageManager;
|
|
8
|
+
private runAudit;
|
|
9
|
+
private normaliseAuditOutput;
|
|
10
|
+
private isNpmV7Format;
|
|
11
|
+
private normalisePnpmAudit;
|
|
12
|
+
private normaliseNpmV7Audit;
|
|
13
|
+
private normaliseYarnAudit;
|
|
14
|
+
private calculateScore;
|
|
15
|
+
private generateFixSuggestions;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=dependency.check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency.check.d.ts","sourceRoot":"","sources":["../../../src/gate/checks/dependency.check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AA2JrE,qBAAa,eAAgB,SAAQ,SAAS;IAC5C,IAAI,SAAgB;IACpB,WAAW,SAAmE;IAExE,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IA8HrD,OAAO,CAAC,oBAAoB;YAad,QAAQ;IAkDtB,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,mBAAmB;IAyC3B,OAAO,CAAC,kBAAkB;IAyC1B,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,sBAAsB;CAyC/B"}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { BaseCheck } from '../check.interface.js';
|
|
2
|
+
import { createDebugger } from '@eddacraft/anvil-core';
|
|
3
|
+
import { exec } from 'node:child_process';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
const log = createDebugger('check');
|
|
9
|
+
const execAsync = promisify(exec);
|
|
10
|
+
const YarnAdvisoryDataSchema = z.object({
|
|
11
|
+
id: z.number(),
|
|
12
|
+
title: z.string(),
|
|
13
|
+
module_name: z.string(),
|
|
14
|
+
severity: z.enum(['info', 'low', 'moderate', 'high', 'critical']),
|
|
15
|
+
url: z.string(),
|
|
16
|
+
cves: z.array(z.string()),
|
|
17
|
+
vulnerable_versions: z.string(),
|
|
18
|
+
patched_versions: z.string(),
|
|
19
|
+
recommendation: z.string(),
|
|
20
|
+
findings: z.array(z.object({ version: z.string(), paths: z.array(z.string()) })),
|
|
21
|
+
});
|
|
22
|
+
const YarnClassicAdvisorySchema = z.object({
|
|
23
|
+
type: z.literal('auditAdvisory'),
|
|
24
|
+
data: z.object({
|
|
25
|
+
advisory: YarnAdvisoryDataSchema,
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
const YarnClassicSummarySchema = z.object({
|
|
29
|
+
type: z.literal('auditSummary'),
|
|
30
|
+
data: z.object({
|
|
31
|
+
vulnerabilities: z.object({
|
|
32
|
+
info: z.number(),
|
|
33
|
+
low: z.number(),
|
|
34
|
+
moderate: z.number(),
|
|
35
|
+
high: z.number(),
|
|
36
|
+
critical: z.number(),
|
|
37
|
+
total: z.number(),
|
|
38
|
+
}),
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
const YarnAuditLineSchema = z.union([YarnClassicAdvisorySchema, YarnClassicSummarySchema]);
|
|
42
|
+
const SEVERITY_SCORES = {
|
|
43
|
+
critical: 100,
|
|
44
|
+
high: 75,
|
|
45
|
+
moderate: 50,
|
|
46
|
+
low: 25,
|
|
47
|
+
info: 10,
|
|
48
|
+
};
|
|
49
|
+
const SEVERITY_ORDER = ['critical', 'high', 'moderate', 'low', 'info'];
|
|
50
|
+
const VULNERABILITY_SCORE_PENALTIES = {
|
|
51
|
+
critical: 20,
|
|
52
|
+
high: 10,
|
|
53
|
+
moderate: 5,
|
|
54
|
+
low: 2,
|
|
55
|
+
};
|
|
56
|
+
export class DependencyCheck extends BaseCheck {
|
|
57
|
+
name = 'dependency';
|
|
58
|
+
description = 'Scan for dependency vulnerabilities using npm/yarn/pnpm audit';
|
|
59
|
+
async run(context) {
|
|
60
|
+
log(`dependency check starting, workspace=${context.workspace_root}`);
|
|
61
|
+
try {
|
|
62
|
+
// Check if package.json exists
|
|
63
|
+
const packageJsonPath = join(context.workspace_root, 'package.json');
|
|
64
|
+
if (!existsSync(packageJsonPath)) {
|
|
65
|
+
log('dependency check: no package.json found, skipping');
|
|
66
|
+
return this.createSuccess('No package.json found, skipping dependency check', 100);
|
|
67
|
+
}
|
|
68
|
+
// Detect package manager
|
|
69
|
+
const packageManager = this.detectPackageManager(context.workspace_root);
|
|
70
|
+
if (!packageManager) {
|
|
71
|
+
log('dependency check: no lock file found, skipping');
|
|
72
|
+
return this.createSuccess('No lock file found, skipping dependency check', 100);
|
|
73
|
+
}
|
|
74
|
+
log(`dependency check: detected package manager=${packageManager}`);
|
|
75
|
+
// Determine severity threshold from config (default: moderate)
|
|
76
|
+
const minSeverity = context.check_config.min_severity || 'moderate';
|
|
77
|
+
const severityIndex = SEVERITY_ORDER.indexOf(minSeverity);
|
|
78
|
+
// Run audit with detected package manager
|
|
79
|
+
const auditResult = await this.runAudit(context.workspace_root, packageManager);
|
|
80
|
+
if (!auditResult) {
|
|
81
|
+
log('dependency check: no vulnerabilities found');
|
|
82
|
+
return this.createSuccess('No vulnerabilities found', 100);
|
|
83
|
+
}
|
|
84
|
+
const advisories = Object.values(auditResult.advisories);
|
|
85
|
+
const metadata = auditResult.metadata.vulnerabilities;
|
|
86
|
+
log('dependency check: found advisories', {
|
|
87
|
+
count: advisories.length,
|
|
88
|
+
total: metadata.total,
|
|
89
|
+
critical: metadata.critical,
|
|
90
|
+
high: metadata.high,
|
|
91
|
+
moderate: metadata.moderate,
|
|
92
|
+
low: metadata.low,
|
|
93
|
+
});
|
|
94
|
+
// Filter vulnerabilities by severity threshold
|
|
95
|
+
const relevantVulns = advisories.filter((advisory) => {
|
|
96
|
+
const vulnSeverityIndex = SEVERITY_ORDER.indexOf(advisory.severity);
|
|
97
|
+
return vulnSeverityIndex <= severityIndex;
|
|
98
|
+
});
|
|
99
|
+
// Sort by severity (critical first)
|
|
100
|
+
relevantVulns.sort((a, b) => {
|
|
101
|
+
const aScore = SEVERITY_SCORES[a.severity];
|
|
102
|
+
const bScore = SEVERITY_SCORES[b.severity];
|
|
103
|
+
return bScore - aScore;
|
|
104
|
+
});
|
|
105
|
+
// Calculate score based on vulnerabilities
|
|
106
|
+
const criticalCount = metadata.critical;
|
|
107
|
+
const highCount = metadata.high;
|
|
108
|
+
const moderateCount = metadata.moderate;
|
|
109
|
+
const lowCount = metadata.low;
|
|
110
|
+
const score = this.calculateScore(criticalCount, highCount, moderateCount, lowCount);
|
|
111
|
+
// Determine if check passed
|
|
112
|
+
const failOnCritical = context.check_config.fail_on_critical !== false;
|
|
113
|
+
const failOnHigh = context.check_config.fail_on_high !== false;
|
|
114
|
+
const failOnModerate = context.check_config.fail_on_moderate === true;
|
|
115
|
+
const hasCritical = criticalCount > 0;
|
|
116
|
+
const hasHigh = highCount > 0;
|
|
117
|
+
const hasModerate = moderateCount > 0;
|
|
118
|
+
const passed = !(failOnCritical && hasCritical) &&
|
|
119
|
+
!(failOnHigh && hasHigh) &&
|
|
120
|
+
!(failOnModerate && hasModerate);
|
|
121
|
+
log(`dependency check result: passed=${passed}, score=${score}, relevant=${relevantVulns.length}, minSeverity=${minSeverity}`);
|
|
122
|
+
const totalRelevant = relevantVulns.length;
|
|
123
|
+
const message = passed
|
|
124
|
+
? `Dependency check passed: ${metadata.total} vulnerabilities found (${criticalCount} critical, ${highCount} high, ${moderateCount} moderate, ${lowCount} low)`
|
|
125
|
+
: `Dependency check failed: ${totalRelevant} vulnerabilities exceed threshold (${criticalCount} critical, ${highCount} high, ${moderateCount} moderate)`;
|
|
126
|
+
// Generate fix suggestions
|
|
127
|
+
const fixSuggestions = this.generateFixSuggestions(relevantVulns, packageManager);
|
|
128
|
+
return this.createResult(passed, message, score, {
|
|
129
|
+
total: metadata.total,
|
|
130
|
+
critical: criticalCount,
|
|
131
|
+
high: highCount,
|
|
132
|
+
moderate: moderateCount,
|
|
133
|
+
low: lowCount,
|
|
134
|
+
info: metadata.info,
|
|
135
|
+
packageManager,
|
|
136
|
+
vulnerabilities: relevantVulns.map((adv) => ({
|
|
137
|
+
id: adv.id,
|
|
138
|
+
title: adv.title,
|
|
139
|
+
severity: adv.severity,
|
|
140
|
+
module: adv.module_name,
|
|
141
|
+
cves: adv.cves,
|
|
142
|
+
url: adv.url,
|
|
143
|
+
vulnerableVersions: adv.vulnerable_versions,
|
|
144
|
+
patchedVersions: adv.patched_versions,
|
|
145
|
+
recommendation: adv.recommendation,
|
|
146
|
+
})),
|
|
147
|
+
fixSuggestions,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
// If audit command fails, it might be due to no vulnerabilities or command error
|
|
152
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
153
|
+
log(`dependency check error: ${errorMessage}`);
|
|
154
|
+
// pnpm audit exits with code 1 if vulnerabilities are found
|
|
155
|
+
// Check if it's a legitimate error or just vulnerabilities found
|
|
156
|
+
if (errorMessage.includes('EAUDITNOLOCK')) {
|
|
157
|
+
return this.createSuccess('No lock file found, skipping dependency check', 100);
|
|
158
|
+
}
|
|
159
|
+
return this.createFailure('Dependency check failed', errorMessage);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
detectPackageManager(workspaceRoot) {
|
|
163
|
+
if (existsSync(join(workspaceRoot, 'pnpm-lock.yaml'))) {
|
|
164
|
+
return 'pnpm';
|
|
165
|
+
}
|
|
166
|
+
if (existsSync(join(workspaceRoot, 'yarn.lock'))) {
|
|
167
|
+
return 'yarn';
|
|
168
|
+
}
|
|
169
|
+
if (existsSync(join(workspaceRoot, 'package-lock.json'))) {
|
|
170
|
+
return 'npm';
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
async runAudit(workspaceRoot, packageManager) {
|
|
175
|
+
const auditCommand = `${packageManager} audit --json`;
|
|
176
|
+
try {
|
|
177
|
+
const { stdout } = await execAsync(auditCommand, {
|
|
178
|
+
cwd: workspaceRoot,
|
|
179
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
180
|
+
timeout: 120_000,
|
|
181
|
+
});
|
|
182
|
+
return this.normaliseAuditOutput(stdout, packageManager);
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
if (error && typeof error === 'object' && 'stdout' in error) {
|
|
186
|
+
const stdout = error.stdout;
|
|
187
|
+
if (stdout) {
|
|
188
|
+
try {
|
|
189
|
+
return this.normaliseAuditOutput(stdout, packageManager);
|
|
190
|
+
}
|
|
191
|
+
catch (parseError) {
|
|
192
|
+
console.error(`[DependencyCheck] Failed to parse ${packageManager} audit output:`, parseError);
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (error &&
|
|
198
|
+
typeof error === 'object' &&
|
|
199
|
+
'stderr' in error &&
|
|
200
|
+
typeof error.stderr === 'string') {
|
|
201
|
+
const stderr = error.stderr;
|
|
202
|
+
if (stderr.includes('No known vulnerabilities found') ||
|
|
203
|
+
stderr.includes('0 vulnerabilities') ||
|
|
204
|
+
stderr.includes('found 0 vulnerabilities')) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
normaliseAuditOutput(stdout, packageManager) {
|
|
212
|
+
if (packageManager === 'yarn') {
|
|
213
|
+
return this.normaliseYarnAudit(stdout);
|
|
214
|
+
}
|
|
215
|
+
const parsed = JSON.parse(stdout);
|
|
216
|
+
if (packageManager === 'npm' && this.isNpmV7Format(parsed)) {
|
|
217
|
+
return this.normaliseNpmV7Audit(parsed);
|
|
218
|
+
}
|
|
219
|
+
return this.normalisePnpmAudit(parsed);
|
|
220
|
+
}
|
|
221
|
+
isNpmV7Format(parsed) {
|
|
222
|
+
return (typeof parsed === 'object' &&
|
|
223
|
+
parsed !== null &&
|
|
224
|
+
'auditReportVersion' in parsed &&
|
|
225
|
+
parsed.auditReportVersion >= 2);
|
|
226
|
+
}
|
|
227
|
+
normalisePnpmAudit(parsed) {
|
|
228
|
+
if (!parsed.advisories || !parsed.metadata?.vulnerabilities) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
advisories: parsed.advisories,
|
|
233
|
+
metadata: { vulnerabilities: parsed.metadata.vulnerabilities },
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
normaliseNpmV7Audit(parsed) {
|
|
237
|
+
if (!parsed.vulnerabilities || !parsed.metadata?.vulnerabilities) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
const advisories = {};
|
|
241
|
+
let idCounter = 1;
|
|
242
|
+
for (const [moduleName, vuln] of Object.entries(parsed.vulnerabilities)) {
|
|
243
|
+
const viaDetails = vuln.via.find((v) => typeof v === 'object' && v !== null);
|
|
244
|
+
advisories[String(idCounter)] = {
|
|
245
|
+
id: idCounter,
|
|
246
|
+
title: viaDetails?.title || `Vulnerability in ${moduleName}`,
|
|
247
|
+
severity: vuln.severity,
|
|
248
|
+
url: viaDetails?.url || '',
|
|
249
|
+
cves: viaDetails?.cwe || [],
|
|
250
|
+
module_name: moduleName,
|
|
251
|
+
vulnerable_versions: vuln.range,
|
|
252
|
+
patched_versions: typeof vuln.fixAvailable === 'object' ? `>=${vuln.fixAvailable.version}` : '',
|
|
253
|
+
recommendation: typeof vuln.fixAvailable === 'object'
|
|
254
|
+
? `Update to ${vuln.fixAvailable.name}@${vuln.fixAvailable.version}`
|
|
255
|
+
: vuln.fixAvailable
|
|
256
|
+
? 'Run npm audit fix'
|
|
257
|
+
: 'No fix available',
|
|
258
|
+
findings: vuln.nodes.map((node) => ({ version: vuln.range, paths: [node] })),
|
|
259
|
+
};
|
|
260
|
+
idCounter++;
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
advisories,
|
|
264
|
+
metadata: { vulnerabilities: parsed.metadata.vulnerabilities },
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
normaliseYarnAudit(stdout) {
|
|
268
|
+
const lines = stdout.trim().split('\n');
|
|
269
|
+
const advisories = {};
|
|
270
|
+
let metadata = {
|
|
271
|
+
info: 0,
|
|
272
|
+
low: 0,
|
|
273
|
+
moderate: 0,
|
|
274
|
+
high: 0,
|
|
275
|
+
critical: 0,
|
|
276
|
+
total: 0,
|
|
277
|
+
};
|
|
278
|
+
for (const line of lines) {
|
|
279
|
+
if (!line.trim())
|
|
280
|
+
continue;
|
|
281
|
+
try {
|
|
282
|
+
const parseResult = YarnAuditLineSchema.safeParse(JSON.parse(line));
|
|
283
|
+
if (!parseResult.success) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
const parsed = parseResult.data;
|
|
287
|
+
if (parsed.type === 'auditAdvisory') {
|
|
288
|
+
const advisory = parsed.data.advisory;
|
|
289
|
+
advisories[String(advisory.id)] = advisory;
|
|
290
|
+
}
|
|
291
|
+
else if (parsed.type === 'auditSummary') {
|
|
292
|
+
metadata = parsed.data.vulnerabilities;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch (parseError) {
|
|
296
|
+
console.error('[DependencyCheck] Failed to parse Yarn audit line:', parseError);
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (Object.keys(advisories).length === 0 && metadata.total === 0) {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
return { advisories, metadata: { vulnerabilities: metadata } };
|
|
304
|
+
}
|
|
305
|
+
calculateScore(critical, high, moderate, low) {
|
|
306
|
+
let score = 100;
|
|
307
|
+
score -= critical * VULNERABILITY_SCORE_PENALTIES.critical;
|
|
308
|
+
score -= high * VULNERABILITY_SCORE_PENALTIES.high;
|
|
309
|
+
score -= moderate * VULNERABILITY_SCORE_PENALTIES.moderate;
|
|
310
|
+
score -= low * VULNERABILITY_SCORE_PENALTIES.low;
|
|
311
|
+
return Math.max(0, score);
|
|
312
|
+
}
|
|
313
|
+
generateFixSuggestions(vulnerabilities, packageManager) {
|
|
314
|
+
const suggestions = [];
|
|
315
|
+
// Group by module
|
|
316
|
+
const moduleMap = new Map();
|
|
317
|
+
for (const vuln of vulnerabilities) {
|
|
318
|
+
const existing = moduleMap.get(vuln.module_name) || [];
|
|
319
|
+
existing.push(vuln);
|
|
320
|
+
moduleMap.set(vuln.module_name, existing);
|
|
321
|
+
}
|
|
322
|
+
// Generate suggestions for each module
|
|
323
|
+
for (const [module, vulns] of Array.from(moduleMap.entries())) {
|
|
324
|
+
const highestSeverity = vulns[0].severity; // Already sorted by severity
|
|
325
|
+
const patchedVersions = vulns[0].patched_versions;
|
|
326
|
+
if (patchedVersions && patchedVersions !== '<0.0.0') {
|
|
327
|
+
suggestions.push(`Update ${module} to ${patchedVersions} (fixes ${vulns.length} ${highestSeverity} vulnerability(ies))`);
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
suggestions.push(`Review ${module}: No patch available for ${vulns.length} ${highestSeverity} vulnerability(ies)`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Add package manager-specific fix command
|
|
334
|
+
if (suggestions.length > 0) {
|
|
335
|
+
const fixCommand = packageManager === 'yarn'
|
|
336
|
+
? `${packageManager} audit fix` // yarn uses 'audit fix'
|
|
337
|
+
: `${packageManager} audit fix`; // npm and pnpm both use 'audit fix'
|
|
338
|
+
suggestions.push(`Run \`${fixCommand}\` to automatically apply available patches`);
|
|
339
|
+
}
|
|
340
|
+
return suggestions;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BaseCheck } from '../check.interface.js';
|
|
2
|
+
import { CheckContext, GateResult } from '../../types/gate.types.js';
|
|
3
|
+
export declare class ESLintCheck extends BaseCheck {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
run(context: CheckContext): Promise<GateResult>;
|
|
7
|
+
private getFilesFromPlan;
|
|
8
|
+
private isLintableFile;
|
|
9
|
+
/**
|
|
10
|
+
* Get all lintable files for full codebase scan
|
|
11
|
+
*/
|
|
12
|
+
private getFilesForFullScan;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=eslint.check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eslint.check.d.ts","sourceRoot":"","sources":["../../../src/gate/checks/eslint.check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAuB,MAAM,2BAA2B,CAAC;AAM1F,qBAAa,WAAY,SAAQ,SAAS;IACxC,IAAI,SAAY;IAChB,WAAW,SAAoC;IAEzC,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAqErD,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,cAAc;IAKtB;;OAEG;YACW,mBAAmB;CAKlC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { BaseCheck } from '../check.interface.js';
|
|
2
|
+
import { getFilesFromContext } from '../../types/gate.types.js';
|
|
3
|
+
import { createDebugger } from '@eddacraft/anvil-core';
|
|
4
|
+
import { ESLint } from 'eslint';
|
|
5
|
+
const log = createDebugger('check');
|
|
6
|
+
export class ESLintCheck extends BaseCheck {
|
|
7
|
+
name = 'eslint';
|
|
8
|
+
description = 'Run ESLint code quality checks';
|
|
9
|
+
async run(context) {
|
|
10
|
+
log(`eslint check starting, workspace=${context.workspace_root}, fullScan=${context.fullScan}`);
|
|
11
|
+
try {
|
|
12
|
+
const eslint = new ESLint({
|
|
13
|
+
cwd: context.workspace_root,
|
|
14
|
+
});
|
|
15
|
+
// Get files to lint - either from plan or full codebase scan
|
|
16
|
+
const files = context.fullScan
|
|
17
|
+
? await this.getFilesForFullScan(context.workspace_root)
|
|
18
|
+
: this.getFilesFromPlan(context);
|
|
19
|
+
if (files.length === 0) {
|
|
20
|
+
log('eslint check: no lintable files found');
|
|
21
|
+
return this.createSuccess('No files to lint', 100);
|
|
22
|
+
}
|
|
23
|
+
log(`eslint check: linting ${files.length} file(s)`);
|
|
24
|
+
const results = await eslint.lintFiles(files);
|
|
25
|
+
const errorCount = results.reduce((acc, result) => acc + result.errorCount, 0);
|
|
26
|
+
const warningCount = results.reduce((acc, result) => acc + result.warningCount, 0);
|
|
27
|
+
const fixableCount = results.reduce((acc, result) => acc + result.fixableErrorCount + result.fixableWarningCount, 0);
|
|
28
|
+
const totalIssues = errorCount + warningCount;
|
|
29
|
+
const score = totalIssues === 0 ? 100 : Math.max(0, 100 - (errorCount * 10 + warningCount * 2));
|
|
30
|
+
const minScore = typeof context.check_config.min_score === 'number' ? context.check_config.min_score : 80;
|
|
31
|
+
const passed = errorCount === 0 && score >= minScore;
|
|
32
|
+
log('eslint check result', {
|
|
33
|
+
passed,
|
|
34
|
+
score,
|
|
35
|
+
errors: errorCount,
|
|
36
|
+
warnings: warningCount,
|
|
37
|
+
fixable: fixableCount,
|
|
38
|
+
filesLinted: results.length,
|
|
39
|
+
});
|
|
40
|
+
const message = passed
|
|
41
|
+
? `ESLint passed: ${errorCount} errors, ${warningCount} warnings`
|
|
42
|
+
: `ESLint failed: ${errorCount} errors, ${warningCount} warnings`;
|
|
43
|
+
return this.createResult(passed, message, score, {
|
|
44
|
+
errorCount,
|
|
45
|
+
warningCount,
|
|
46
|
+
fixableCount,
|
|
47
|
+
results: results.map((r) => ({
|
|
48
|
+
filePath: r.filePath,
|
|
49
|
+
errorCount: r.errorCount,
|
|
50
|
+
warningCount: r.warningCount,
|
|
51
|
+
messages: r.messages,
|
|
52
|
+
})),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
log(`eslint check error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
57
|
+
return this.createFailure('ESLint check failed', error instanceof Error ? error.message : 'Unknown error');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
getFilesFromPlan(context) {
|
|
61
|
+
// Use unified helper for both planless and plan-based modes
|
|
62
|
+
// This ensures consistent path normalisation and existence checking
|
|
63
|
+
return getFilesFromContext(context, {
|
|
64
|
+
filter: (f) => this.isLintableFile(f),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
isLintableFile(filePath) {
|
|
68
|
+
const lintableExtensions = ['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs'];
|
|
69
|
+
return lintableExtensions.some((ext) => filePath.endsWith(ext));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get all lintable files for full codebase scan
|
|
73
|
+
*/
|
|
74
|
+
async getFilesForFullScan(workspaceRoot) {
|
|
75
|
+
// Use ESLint's file resolution - pass the workspace root
|
|
76
|
+
// ESLint will use its own config to determine which files to lint
|
|
77
|
+
return [workspaceRoot];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Check - Evaluate OPA/Rego policies against plans
|
|
3
|
+
*/
|
|
4
|
+
import { BaseCheck } from '../check.interface.js';
|
|
5
|
+
import { CheckContext, GateResult } from '../../types/gate.types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for policy check
|
|
8
|
+
*/
|
|
9
|
+
export interface PolicyCheckConfig {
|
|
10
|
+
/** Policy directory relative to workspace root */
|
|
11
|
+
policy_dir?: string;
|
|
12
|
+
/** Minimum severity to fail the check */
|
|
13
|
+
severity_threshold?: 'error' | 'warning' | 'info';
|
|
14
|
+
/** Policies to enable (if empty, all are enabled) */
|
|
15
|
+
enabled_policies?: string[];
|
|
16
|
+
/** Policies to disable */
|
|
17
|
+
disabled_policies?: string[];
|
|
18
|
+
/** Custom query (default: data.anvil.policies) */
|
|
19
|
+
query?: string;
|
|
20
|
+
/** Timeout in milliseconds */
|
|
21
|
+
timeout?: number;
|
|
22
|
+
/** Require policy tests to pass before evaluating policies */
|
|
23
|
+
require_policy_tests?: boolean;
|
|
24
|
+
/** Include git context in OPA input for repository-aware policies */
|
|
25
|
+
include_git_context?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Policy check that evaluates OPA/Rego policies against plans
|
|
29
|
+
*/
|
|
30
|
+
export declare class PolicyCheck extends BaseCheck {
|
|
31
|
+
name: string;
|
|
32
|
+
description: string;
|
|
33
|
+
private policyLoader;
|
|
34
|
+
constructor();
|
|
35
|
+
run(context: CheckContext): Promise<GateResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Parse and validate check configuration
|
|
38
|
+
*/
|
|
39
|
+
private parseConfig;
|
|
40
|
+
/**
|
|
41
|
+
* Build OPA input from check context
|
|
42
|
+
*/
|
|
43
|
+
private buildOPAInput;
|
|
44
|
+
/**
|
|
45
|
+
* Bridges ArchitectureCheck output to PolicyCheck OPA input.
|
|
46
|
+
* Enables Rego policies to query architecture context via input.architecture
|
|
47
|
+
*/
|
|
48
|
+
private buildArchitectureInput;
|
|
49
|
+
/**
|
|
50
|
+
* Get git context for repository-aware policies
|
|
51
|
+
*/
|
|
52
|
+
private getGitContext;
|
|
53
|
+
/**
|
|
54
|
+
* Get CI context from environment variables
|
|
55
|
+
*/
|
|
56
|
+
private getCIContext;
|
|
57
|
+
/**
|
|
58
|
+
* Extract PR number from GitHub ref (e.g., refs/pull/123/merge)
|
|
59
|
+
*/
|
|
60
|
+
private extractPRNumber;
|
|
61
|
+
/**
|
|
62
|
+
* Run policy tests and return results
|
|
63
|
+
*/
|
|
64
|
+
private runPolicyTests;
|
|
65
|
+
/**
|
|
66
|
+
* Calculate score based on violations
|
|
67
|
+
*/
|
|
68
|
+
private calculateScore;
|
|
69
|
+
/**
|
|
70
|
+
* Check if a severity level should block the check
|
|
71
|
+
*/
|
|
72
|
+
private isBlockingSeverity;
|
|
73
|
+
/**
|
|
74
|
+
* Build human-readable message
|
|
75
|
+
*/
|
|
76
|
+
private buildMessage;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=policy.check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.check.d.ts","sourceRoot":"","sources":["../../../src/gate/checks/policy.check.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAarE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IAClD,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,0BAA0B;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qEAAqE;IACrE,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAgBD;;GAEG;AACH,qBAAa,WAAY,SAAQ,SAAS;IACxC,IAAI,SAAY;IAChB,WAAW,SAA8C;IAEzD,OAAO,CAAC,YAAY,CAAe;;IAO7B,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IA4IrD;;OAEG;IACH,OAAO,CAAC,WAAW;IAuBnB;;OAEG;IACH,OAAO,CAAC,aAAa;IA8DrB;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IA+C9B;;OAEG;IACH,OAAO,CAAC,aAAa;IAyDrB;;OAEG;IACH,OAAO,CAAC,YAAY;IA+CpB;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;OAEG;YACW,cAAc;IAmC5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAsCtB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;IACH,OAAO,CAAC,YAAY;CAqBrB"}
|