@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
|
+
/**
|
|
2
|
+
* Dependency Analyzer - Wrapper for dependency-cruiser
|
|
3
|
+
*
|
|
4
|
+
* Handles loading and executing dependency-cruiser for architectural analysis.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Violation from dependency-cruiser
|
|
8
|
+
*/
|
|
9
|
+
export interface CruiserViolation {
|
|
10
|
+
from: string;
|
|
11
|
+
to: string;
|
|
12
|
+
rule: {
|
|
13
|
+
name: string;
|
|
14
|
+
severity: 'error' | 'warn' | 'info' | 'ignore';
|
|
15
|
+
};
|
|
16
|
+
cycle?: string[];
|
|
17
|
+
comment?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Summary from dependency-cruiser
|
|
21
|
+
*/
|
|
22
|
+
export interface CruiserSummary {
|
|
23
|
+
violations: CruiserViolation[];
|
|
24
|
+
error: number;
|
|
25
|
+
warn: number;
|
|
26
|
+
info: number;
|
|
27
|
+
totalCruised: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Cruise result from dependency-cruiser
|
|
31
|
+
*/
|
|
32
|
+
export interface ICruiseResult {
|
|
33
|
+
summary: CruiserSummary;
|
|
34
|
+
modules: unknown[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Cruise function type
|
|
38
|
+
*/
|
|
39
|
+
export type CruiseFn = (fileAndDirectoryArray: string[], options?: Record<string, unknown>) => Promise<{
|
|
40
|
+
output: ICruiseResult;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* Result of dependency analysis
|
|
44
|
+
*/
|
|
45
|
+
export interface AnalysisResult {
|
|
46
|
+
success: boolean;
|
|
47
|
+
result?: ICruiseResult;
|
|
48
|
+
error?: string;
|
|
49
|
+
skipped?: boolean;
|
|
50
|
+
reason?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Dependency analyzer that wraps dependency-cruiser
|
|
54
|
+
*/
|
|
55
|
+
export declare class DependencyAnalyzer {
|
|
56
|
+
private cruiseFn;
|
|
57
|
+
/**
|
|
58
|
+
* Load dependency-cruiser dynamically
|
|
59
|
+
*/
|
|
60
|
+
loadCruiser(): Promise<{
|
|
61
|
+
success: boolean;
|
|
62
|
+
error?: string;
|
|
63
|
+
}>;
|
|
64
|
+
/**
|
|
65
|
+
* Check if dependency-cruiser is available
|
|
66
|
+
*/
|
|
67
|
+
isAvailable(): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Load dependency-cruiser configuration
|
|
70
|
+
*/
|
|
71
|
+
loadConfig(workspaceRoot: string, configFile: string): Promise<Record<string, unknown> | null>;
|
|
72
|
+
/**
|
|
73
|
+
* Get default cruise options when no config file exists
|
|
74
|
+
*/
|
|
75
|
+
getDefaultCruiseOptions(): Record<string, unknown>;
|
|
76
|
+
/**
|
|
77
|
+
* Run dependency analysis
|
|
78
|
+
*/
|
|
79
|
+
analyze(filesToCruise: string[], cruiseOptions: Record<string, unknown>): Promise<AnalysisResult>;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=dependency-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency-analyzer.d.ts","sourceRoot":"","sources":["../../../../src/gate/checks/architecture/dependency-analyzer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;KAChD,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,cAAc,CAAC;IACxB,OAAO,EAAE,OAAO,EAAE,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,CACrB,qBAAqB,EAAE,MAAM,EAAE,EAC/B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC9B,OAAO,CAAC;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC;AAExC;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAyB;IAEzC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAoBlE;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACG,UAAU,CACd,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAkB1C;;OAEG;IACH,uBAAuB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAkClD;;OAEG;IACG,OAAO,CACX,aAAa,EAAE,MAAM,EAAE,EACvB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,OAAO,CAAC,cAAc,CAAC;CAwC3B"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Analyzer - Wrapper for dependency-cruiser
|
|
3
|
+
*
|
|
4
|
+
* Handles loading and executing dependency-cruiser for architectural analysis.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { createDebugger } from '@eddacraft/anvil-core';
|
|
9
|
+
const log = createDebugger('check');
|
|
10
|
+
/**
|
|
11
|
+
* Dependency analyzer that wraps dependency-cruiser
|
|
12
|
+
*/
|
|
13
|
+
export class DependencyAnalyzer {
|
|
14
|
+
cruiseFn = null;
|
|
15
|
+
/**
|
|
16
|
+
* Load dependency-cruiser dynamically
|
|
17
|
+
*/
|
|
18
|
+
async loadCruiser() {
|
|
19
|
+
log('dependency-analyzer: loading dependency-cruiser');
|
|
20
|
+
try {
|
|
21
|
+
// Dynamic import - dependency-cruiser is an optional peer dependency
|
|
22
|
+
// Using Function constructor to avoid bundler static analysis
|
|
23
|
+
const depCruiser = (await Function('return import("dependency-cruiser")')());
|
|
24
|
+
this.cruiseFn = depCruiser.cruise;
|
|
25
|
+
log('dependency-analyzer: dependency-cruiser loaded successfully');
|
|
26
|
+
return { success: true };
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
log('dependency-analyzer: dependency-cruiser not installed');
|
|
30
|
+
return {
|
|
31
|
+
success: false,
|
|
32
|
+
error: 'dependency-cruiser not installed',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if dependency-cruiser is available
|
|
38
|
+
*/
|
|
39
|
+
isAvailable() {
|
|
40
|
+
return this.cruiseFn !== null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Load dependency-cruiser configuration
|
|
44
|
+
*/
|
|
45
|
+
async loadConfig(workspaceRoot, configFile) {
|
|
46
|
+
const configPath = join(workspaceRoot, configFile);
|
|
47
|
+
if (!existsSync(configPath)) {
|
|
48
|
+
log(`dependency-analyzer: config file not found at ${configPath}`);
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const configModule = await import(configPath);
|
|
53
|
+
log(`dependency-analyzer: config loaded from ${configPath}`);
|
|
54
|
+
return configModule.default || configModule;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
log(`dependency-analyzer: failed to load config from ${configPath}`);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get default cruise options when no config file exists
|
|
63
|
+
*/
|
|
64
|
+
getDefaultCruiseOptions() {
|
|
65
|
+
return {
|
|
66
|
+
validate: true,
|
|
67
|
+
ruleSet: {
|
|
68
|
+
forbidden: [
|
|
69
|
+
{
|
|
70
|
+
name: 'no-circular',
|
|
71
|
+
severity: 'error',
|
|
72
|
+
comment: 'Circular dependencies are not allowed',
|
|
73
|
+
from: {},
|
|
74
|
+
to: {
|
|
75
|
+
circular: true,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'no-orphans',
|
|
80
|
+
severity: 'warn',
|
|
81
|
+
comment: 'Modules without dependents or dependencies',
|
|
82
|
+
from: {
|
|
83
|
+
orphan: true,
|
|
84
|
+
pathNot: [
|
|
85
|
+
'\\.d\\.ts$',
|
|
86
|
+
'\\.test\\.(ts|js)$',
|
|
87
|
+
'\\.spec\\.(ts|js)$',
|
|
88
|
+
'index\\.(ts|js)$',
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
to: {},
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Run dependency analysis
|
|
99
|
+
*/
|
|
100
|
+
async analyze(filesToCruise, cruiseOptions) {
|
|
101
|
+
if (!this.cruiseFn) {
|
|
102
|
+
log('dependency-analyzer: cruise function not available, skipping analysis');
|
|
103
|
+
return {
|
|
104
|
+
success: false,
|
|
105
|
+
skipped: true,
|
|
106
|
+
reason: 'dependency-cruiser not available',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
log(`dependency-analyzer: analysing ${filesToCruise.length} files/patterns`);
|
|
110
|
+
try {
|
|
111
|
+
const cruiseResult = await this.cruiseFn(filesToCruise, {
|
|
112
|
+
...cruiseOptions,
|
|
113
|
+
outputType: 'json',
|
|
114
|
+
});
|
|
115
|
+
const summary = cruiseResult.output.summary;
|
|
116
|
+
log('dependency-analyzer: analysis complete', {
|
|
117
|
+
totalCruised: summary.totalCruised,
|
|
118
|
+
violations: summary.violations.length,
|
|
119
|
+
error: summary.error,
|
|
120
|
+
warn: summary.warn,
|
|
121
|
+
info: summary.info,
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
success: true,
|
|
125
|
+
result: cruiseResult.output,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
log(`dependency-analyzer: analysis failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
130
|
+
return {
|
|
131
|
+
success: false,
|
|
132
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer Validator - Score calculation and validation for architecture violations
|
|
3
|
+
*
|
|
4
|
+
* Handles scoring, pass/fail determination, and severity-based blocking.
|
|
5
|
+
*/
|
|
6
|
+
import type { CruiserViolation } from './dependency-analyzer.js';
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for architecture check
|
|
9
|
+
*/
|
|
10
|
+
export interface ArchitectureCheckConfig {
|
|
11
|
+
/** Path to dependency-cruiser config (default: .anvil/dependency-cruiser.js) */
|
|
12
|
+
config_file?: string;
|
|
13
|
+
/** Scope of analysis: 'affected' | 'full' */
|
|
14
|
+
scope?: 'affected' | 'full';
|
|
15
|
+
/** Minimum severity to fail the check */
|
|
16
|
+
severity_threshold?: 'error' | 'warn' | 'info';
|
|
17
|
+
/** Whether circular dependencies should fail the check */
|
|
18
|
+
fail_on_circular?: boolean;
|
|
19
|
+
/** Whether orphaned modules should fail the check */
|
|
20
|
+
fail_on_orphan?: boolean;
|
|
21
|
+
/** Include patterns (glob) for full scope */
|
|
22
|
+
include_patterns?: string[];
|
|
23
|
+
/** Exclude patterns (glob) */
|
|
24
|
+
exclude_patterns?: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Default configuration
|
|
28
|
+
*/
|
|
29
|
+
export declare const DEFAULT_CONFIG: Required<ArchitectureCheckConfig>;
|
|
30
|
+
/**
|
|
31
|
+
* Result of score calculation
|
|
32
|
+
*/
|
|
33
|
+
export interface ScoreResult {
|
|
34
|
+
score: number;
|
|
35
|
+
passed: boolean;
|
|
36
|
+
violationsByType: Record<string, number>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Layer validator for architecture constraints
|
|
40
|
+
*/
|
|
41
|
+
export declare class LayerValidator {
|
|
42
|
+
/**
|
|
43
|
+
* Parse and validate check configuration
|
|
44
|
+
*/
|
|
45
|
+
parseConfig(checkConfig: Record<string, unknown>): Required<ArchitectureCheckConfig>;
|
|
46
|
+
/**
|
|
47
|
+
* Parse scope option
|
|
48
|
+
*/
|
|
49
|
+
private parseScope;
|
|
50
|
+
/**
|
|
51
|
+
* Parse severity threshold
|
|
52
|
+
*/
|
|
53
|
+
private parseSeverity;
|
|
54
|
+
/**
|
|
55
|
+
* Calculate score based on violations
|
|
56
|
+
*/
|
|
57
|
+
calculateScore(violations: CruiserViolation[], config: Required<ArchitectureCheckConfig>): ScoreResult;
|
|
58
|
+
/**
|
|
59
|
+
* Check if a severity level should block the check
|
|
60
|
+
*/
|
|
61
|
+
private isBlockingSeverity;
|
|
62
|
+
/**
|
|
63
|
+
* Check if a file should be analysed
|
|
64
|
+
*/
|
|
65
|
+
isAnalysableFile(filePath: string, config: Required<ArchitectureCheckConfig>): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Simple glob pattern matching (for common patterns)
|
|
68
|
+
*/
|
|
69
|
+
private matchesGlobPattern;
|
|
70
|
+
/**
|
|
71
|
+
* Build human-readable message
|
|
72
|
+
*/
|
|
73
|
+
buildMessage(violations: CruiserViolation[], totalCruised: number, passed: boolean): string;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=layer-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer-validator.d.ts","sourceRoot":"","sources":["../../../../src/gate/checks/architecture/layer-validator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAKjE;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAC5B,yCAAyC;IACzC,kBAAkB,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/C,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,qDAAqD;IACrD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,6CAA6C;IAC7C,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,8BAA8B;IAC9B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,uBAAuB,CAc5D,CAAC;AAYF;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB;;OAEG;IACH,WAAW,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,uBAAuB,CAAC;IAoBpF;;OAEG;IACH,OAAO,CAAC,UAAU;IAOlB;;OAEG;IACH,OAAO,CAAC,aAAa;IASrB;;OAEG;IACH,cAAc,CACZ,UAAU,EAAE,gBAAgB,EAAE,EAC9B,MAAM,EAAE,QAAQ,CAAC,uBAAuB,CAAC,GACxC,WAAW;IA8Dd;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,uBAAuB,CAAC,GAAG,OAAO;IAgBtF;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAe1B;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,gBAAgB,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM;CAiB5F"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer Validator - Score calculation and validation for architecture violations
|
|
3
|
+
*
|
|
4
|
+
* Handles scoring, pass/fail determination, and severity-based blocking.
|
|
5
|
+
*/
|
|
6
|
+
import { createDebugger } from '@eddacraft/anvil-core';
|
|
7
|
+
const log = createDebugger('check');
|
|
8
|
+
/**
|
|
9
|
+
* Default configuration
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_CONFIG = {
|
|
12
|
+
config_file: '.anvil/dependency-cruiser.js',
|
|
13
|
+
scope: 'affected',
|
|
14
|
+
severity_threshold: 'error',
|
|
15
|
+
fail_on_circular: true,
|
|
16
|
+
fail_on_orphan: false,
|
|
17
|
+
include_patterns: ['src/**/*.ts', 'src/**/*.js'],
|
|
18
|
+
exclude_patterns: [
|
|
19
|
+
'**/*.test.ts',
|
|
20
|
+
'**/*.spec.ts',
|
|
21
|
+
'**/__fixtures__/**',
|
|
22
|
+
'**/__tests__/**',
|
|
23
|
+
'**/node_modules/**',
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Score penalties per severity
|
|
28
|
+
*/
|
|
29
|
+
const SEVERITY_PENALTIES = {
|
|
30
|
+
error: 15,
|
|
31
|
+
warn: 5,
|
|
32
|
+
info: 1,
|
|
33
|
+
ignore: 0,
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Layer validator for architecture constraints
|
|
37
|
+
*/
|
|
38
|
+
export class LayerValidator {
|
|
39
|
+
/**
|
|
40
|
+
* Parse and validate check configuration
|
|
41
|
+
*/
|
|
42
|
+
parseConfig(checkConfig) {
|
|
43
|
+
return {
|
|
44
|
+
config_file: typeof checkConfig.config_file === 'string'
|
|
45
|
+
? checkConfig.config_file
|
|
46
|
+
: DEFAULT_CONFIG.config_file,
|
|
47
|
+
scope: this.parseScope(checkConfig.scope),
|
|
48
|
+
severity_threshold: this.parseSeverity(checkConfig.severity_threshold) || DEFAULT_CONFIG.severity_threshold,
|
|
49
|
+
fail_on_circular: checkConfig.fail_on_circular !== false,
|
|
50
|
+
fail_on_orphan: checkConfig.fail_on_orphan === true,
|
|
51
|
+
include_patterns: Array.isArray(checkConfig.include_patterns)
|
|
52
|
+
? checkConfig.include_patterns.filter((p) => typeof p === 'string')
|
|
53
|
+
: DEFAULT_CONFIG.include_patterns,
|
|
54
|
+
exclude_patterns: Array.isArray(checkConfig.exclude_patterns)
|
|
55
|
+
? checkConfig.exclude_patterns.filter((p) => typeof p === 'string')
|
|
56
|
+
: DEFAULT_CONFIG.exclude_patterns,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Parse scope option
|
|
61
|
+
*/
|
|
62
|
+
parseScope(value) {
|
|
63
|
+
if (value === 'full' || value === 'affected') {
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
return DEFAULT_CONFIG.scope;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Parse severity threshold
|
|
70
|
+
*/
|
|
71
|
+
parseSeverity(value) {
|
|
72
|
+
if (typeof value !== 'string')
|
|
73
|
+
return undefined;
|
|
74
|
+
const lower = value.toLowerCase();
|
|
75
|
+
if (lower === 'error' || lower === 'warn' || lower === 'info') {
|
|
76
|
+
return lower;
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Calculate score based on violations
|
|
82
|
+
*/
|
|
83
|
+
calculateScore(violations, config) {
|
|
84
|
+
log(`layer-validator: calculating score for ${violations.length} violations (threshold=${config.severity_threshold})`);
|
|
85
|
+
const violationsByType = {
|
|
86
|
+
circular: 0,
|
|
87
|
+
orphan: 0,
|
|
88
|
+
layer: 0,
|
|
89
|
+
other: 0,
|
|
90
|
+
};
|
|
91
|
+
let totalPenalty = 0;
|
|
92
|
+
let hasBlockingViolation = false;
|
|
93
|
+
for (const v of violations) {
|
|
94
|
+
// Categorise violation
|
|
95
|
+
if (v.cycle && v.cycle.length > 0) {
|
|
96
|
+
violationsByType.circular++;
|
|
97
|
+
if (config.fail_on_circular &&
|
|
98
|
+
this.isBlockingSeverity(v.rule.severity, config.severity_threshold)) {
|
|
99
|
+
hasBlockingViolation = true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else if (v.rule.name.includes('orphan')) {
|
|
103
|
+
violationsByType.orphan++;
|
|
104
|
+
if (config.fail_on_orphan &&
|
|
105
|
+
this.isBlockingSeverity(v.rule.severity, config.severity_threshold)) {
|
|
106
|
+
hasBlockingViolation = true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else if (v.rule.name.includes('layer') || v.rule.name.includes('boundary')) {
|
|
110
|
+
violationsByType.layer++;
|
|
111
|
+
if (this.isBlockingSeverity(v.rule.severity, config.severity_threshold)) {
|
|
112
|
+
hasBlockingViolation = true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
violationsByType.other++;
|
|
117
|
+
if (this.isBlockingSeverity(v.rule.severity, config.severity_threshold)) {
|
|
118
|
+
hasBlockingViolation = true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Calculate penalty
|
|
122
|
+
totalPenalty += SEVERITY_PENALTIES[v.rule.severity] || 0;
|
|
123
|
+
}
|
|
124
|
+
const score = Math.max(0, 100 - totalPenalty);
|
|
125
|
+
const passed = !hasBlockingViolation;
|
|
126
|
+
log('layer-validator result', {
|
|
127
|
+
score,
|
|
128
|
+
passed,
|
|
129
|
+
penalty: totalPenalty,
|
|
130
|
+
blocking: hasBlockingViolation,
|
|
131
|
+
violationsByType,
|
|
132
|
+
});
|
|
133
|
+
return { score, passed, violationsByType };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if a severity level should block the check
|
|
137
|
+
*/
|
|
138
|
+
isBlockingSeverity(severity, threshold) {
|
|
139
|
+
const levels = { error: 3, warn: 2, info: 1, ignore: 0 };
|
|
140
|
+
return levels[severity] >= levels[threshold];
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Check if a file should be analysed
|
|
144
|
+
*/
|
|
145
|
+
isAnalysableFile(filePath, config) {
|
|
146
|
+
const analysableExtensions = ['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs'];
|
|
147
|
+
const hasValidExtension = analysableExtensions.some((ext) => filePath.endsWith(ext));
|
|
148
|
+
if (!hasValidExtension)
|
|
149
|
+
return false;
|
|
150
|
+
// Check exclusions
|
|
151
|
+
for (const pattern of config.exclude_patterns) {
|
|
152
|
+
if (this.matchesGlobPattern(filePath, pattern)) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Simple glob pattern matching (for common patterns)
|
|
160
|
+
*/
|
|
161
|
+
matchesGlobPattern(filePath, pattern) {
|
|
162
|
+
// Normalise to forward slashes for consistent cross-platform matching
|
|
163
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
164
|
+
// Convert glob to regex (simplified)
|
|
165
|
+
const regexPattern = pattern
|
|
166
|
+
.replace(/\*\*/g, '{{GLOBSTAR}}')
|
|
167
|
+
.replace(/\*/g, '[^/]*')
|
|
168
|
+
.replace(/{{GLOBSTAR}}/g, '.*')
|
|
169
|
+
.replace(/\?/g, '.');
|
|
170
|
+
const regex = new RegExp(regexPattern);
|
|
171
|
+
return regex.test(normalizedPath);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Build human-readable message
|
|
175
|
+
*/
|
|
176
|
+
buildMessage(violations, totalCruised, passed) {
|
|
177
|
+
if (violations.length === 0) {
|
|
178
|
+
return `Architecture check passed: ${totalCruised} modules analysed, no violations`;
|
|
179
|
+
}
|
|
180
|
+
const errorCount = violations.filter((v) => v.rule.severity === 'error').length;
|
|
181
|
+
const warnCount = violations.filter((v) => v.rule.severity === 'warn').length;
|
|
182
|
+
const infoCount = violations.filter((v) => v.rule.severity === 'info').length;
|
|
183
|
+
const parts = [];
|
|
184
|
+
if (errorCount > 0)
|
|
185
|
+
parts.push(`${errorCount} error${errorCount > 1 ? 's' : ''}`);
|
|
186
|
+
if (warnCount > 0)
|
|
187
|
+
parts.push(`${warnCount} warning${warnCount > 1 ? 's' : ''}`);
|
|
188
|
+
if (infoCount > 0)
|
|
189
|
+
parts.push(`${infoCount} info`);
|
|
190
|
+
const status = passed ? 'passed with issues' : 'failed';
|
|
191
|
+
return `Architecture check ${status}: ${parts.join(', ')} (${totalCruised} modules analysed)`;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Architecture Check - Validate architectural constraints using dependency-cruiser
|
|
3
|
+
*
|
|
4
|
+
* Detects:
|
|
5
|
+
* - Circular dependencies
|
|
6
|
+
* - Layer/boundary violations
|
|
7
|
+
* - Orphaned modules (optional)
|
|
8
|
+
*/
|
|
9
|
+
import { BaseCheck } from '../check.interface.js';
|
|
10
|
+
import { CheckContext, GateResult } from '../../types/gate.types.js';
|
|
11
|
+
export type { ArchitectureCheckConfig } from './architecture/layer-validator.js';
|
|
12
|
+
/**
|
|
13
|
+
* Architecture check that validates project structure using dependency-cruiser
|
|
14
|
+
*/
|
|
15
|
+
export declare class ArchitectureCheck extends BaseCheck {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
private analyzer;
|
|
19
|
+
private detector;
|
|
20
|
+
private validator;
|
|
21
|
+
run(context: CheckContext): Promise<GateResult>;
|
|
22
|
+
private convertViolationToWarning;
|
|
23
|
+
private mapCruiserSeverity;
|
|
24
|
+
private createArchWarningResult;
|
|
25
|
+
/**
|
|
26
|
+
* Get files to cruise based on scope
|
|
27
|
+
*/
|
|
28
|
+
private getFilesToCruise;
|
|
29
|
+
/**
|
|
30
|
+
* Check if a file should be analysed
|
|
31
|
+
* @internal Reserved for future use
|
|
32
|
+
*/
|
|
33
|
+
private _isAnalysableFile;
|
|
34
|
+
private matchesGlobPattern;
|
|
35
|
+
/**
|
|
36
|
+
* Get default cruise options when no config file exists
|
|
37
|
+
* @internal Reserved for future use
|
|
38
|
+
*/
|
|
39
|
+
private _getDefaultCruiseOptions;
|
|
40
|
+
/**
|
|
41
|
+
* Calculate score based on violations
|
|
42
|
+
* @internal Reserved for future use
|
|
43
|
+
*/
|
|
44
|
+
private _calculateScore;
|
|
45
|
+
/**
|
|
46
|
+
* Check if a severity level should block the check
|
|
47
|
+
*/
|
|
48
|
+
private isBlockingSeverity;
|
|
49
|
+
/**
|
|
50
|
+
* Build human-readable message
|
|
51
|
+
* @internal Reserved for future use
|
|
52
|
+
*/
|
|
53
|
+
private _buildMessage;
|
|
54
|
+
private buildArchitectureContext;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=architecture.check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"architecture.check.d.ts","sourceRoot":"","sources":["../../../src/gate/checks/architecture.check.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAuB,MAAM,2BAA2B,CAAC;AA0B1F,YAAY,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AA2CjF;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,IAAI,SAAkB;IACtB,WAAW,SAAiE;IAE5E,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,SAAS,CAAwB;IAEnC,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAyJrD,OAAO,CAAC,yBAAyB;IAqCjC,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,uBAAuB;IAU/B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAsBxB;;;OAGG;IAEH,OAAO,CAAC,iBAAiB;IAgBzB,OAAO,CAAC,kBAAkB;IAM1B;;;OAGG;IAEH,OAAO,CAAC,wBAAwB;IAkChC;;;OAGG;IAEH,OAAO,CAAC,eAAe;IA0DvB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;;OAGG;IAEH,OAAO,CAAC,aAAa;IAsBrB,OAAO,CAAC,wBAAwB;CA6CjC"}
|