@stackbilt/drift 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/drift.test.d.ts +2 -0
- package/dist/__tests__/drift.test.d.ts.map +1 -0
- package/dist/__tests__/drift.test.js +108 -0
- package/dist/__tests__/drift.test.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +101 -0
- package/dist/index.js.map +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/drift.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const index_1 = require("../index");
|
|
5
|
+
(0, vitest_1.describe)('extractRules', () => {
|
|
6
|
+
(0, vitest_1.it)('parses regex literals', () => {
|
|
7
|
+
const rules = (0, index_1.extractRules)('Avoid /console\\.log/g in production');
|
|
8
|
+
(0, vitest_1.expect)(rules).toHaveLength(1);
|
|
9
|
+
(0, vitest_1.expect)(rules[0].test('console.log("test")')).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
(0, vitest_1.it)('parses regex with flags', () => {
|
|
12
|
+
const rules = (0, index_1.extractRules)('Do not use /var\\s+/gi');
|
|
13
|
+
(0, vitest_1.expect)(rules).toHaveLength(1);
|
|
14
|
+
(0, vitest_1.expect)(rules[0].flags).toContain('g');
|
|
15
|
+
(0, vitest_1.expect)(rules[0].flags).toContain('i');
|
|
16
|
+
});
|
|
17
|
+
(0, vitest_1.it)('parses backtick keywords', () => {
|
|
18
|
+
const rules = (0, index_1.extractRules)('Do not use `eval` or `Function`');
|
|
19
|
+
(0, vitest_1.expect)(rules).toHaveLength(2);
|
|
20
|
+
(0, vitest_1.expect)(rules[0].test('eval("code")')).toBe(true);
|
|
21
|
+
(0, vitest_1.expect)(rules[1].test('new Function("x")')).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
(0, vitest_1.it)('escapes special regex characters in keywords', () => {
|
|
24
|
+
const rules = (0, index_1.extractRules)('Avoid `foo.bar()`');
|
|
25
|
+
(0, vitest_1.expect)(rules).toHaveLength(1);
|
|
26
|
+
(0, vitest_1.expect)(rules[0].test('foo.bar()')).toBe(true);
|
|
27
|
+
(0, vitest_1.expect)(rules[0].test('fooXbar()')).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
(0, vitest_1.it)('returns empty array for text with no patterns', () => {
|
|
30
|
+
const rules = (0, index_1.extractRules)('This is plain text advice');
|
|
31
|
+
(0, vitest_1.expect)(rules).toEqual([]);
|
|
32
|
+
});
|
|
33
|
+
(0, vitest_1.it)('handles both regex and backtick patterns', () => {
|
|
34
|
+
const rules = (0, index_1.extractRules)('Avoid /TODO/i and `FIXME`');
|
|
35
|
+
(0, vitest_1.expect)(rules).toHaveLength(2);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
(0, vitest_1.describe)('scanForDrift', () => {
|
|
39
|
+
const makePattern = (name, antiPatterns) => ({
|
|
40
|
+
id: '1',
|
|
41
|
+
name,
|
|
42
|
+
category: 'SECURITY',
|
|
43
|
+
blessedSolution: 'Use approved method',
|
|
44
|
+
rationale: null,
|
|
45
|
+
antiPatterns,
|
|
46
|
+
documentationUrl: null,
|
|
47
|
+
relatedLedgerId: null,
|
|
48
|
+
status: 'ACTIVE',
|
|
49
|
+
createdAt: '2025-01-01',
|
|
50
|
+
projectId: null,
|
|
51
|
+
});
|
|
52
|
+
(0, vitest_1.it)('detects violations matching anti-patterns', () => {
|
|
53
|
+
const pattern = makePattern('no-eval', 'Do not use `eval`');
|
|
54
|
+
const files = { 'app.js': 'const result = eval("1+1");' };
|
|
55
|
+
const report = (0, index_1.scanForDrift)(files, [pattern]);
|
|
56
|
+
(0, vitest_1.expect)(report.violations).toHaveLength(1);
|
|
57
|
+
(0, vitest_1.expect)(report.violations[0].file).toBe('app.js');
|
|
58
|
+
(0, vitest_1.expect)(report.violations[0].patternName).toBe('no-eval');
|
|
59
|
+
});
|
|
60
|
+
(0, vitest_1.it)('reports correct line numbers', () => {
|
|
61
|
+
const pattern = makePattern('no-eval', 'Avoid `eval`');
|
|
62
|
+
const files = { 'app.js': 'line1\nline2\neval("bad")\nline4' };
|
|
63
|
+
const report = (0, index_1.scanForDrift)(files, [pattern]);
|
|
64
|
+
(0, vitest_1.expect)(report.violations[0].line).toBe(3);
|
|
65
|
+
});
|
|
66
|
+
(0, vitest_1.it)('returns clean report when no violations found', () => {
|
|
67
|
+
const pattern = makePattern('no-eval', 'Avoid `eval`');
|
|
68
|
+
const files = { 'app.js': 'const x = 1 + 1;' };
|
|
69
|
+
const report = (0, index_1.scanForDrift)(files, [pattern]);
|
|
70
|
+
(0, vitest_1.expect)(report.violations).toEqual([]);
|
|
71
|
+
(0, vitest_1.expect)(report.score).toBe(1.0);
|
|
72
|
+
});
|
|
73
|
+
(0, vitest_1.it)('skips files larger than 500K', () => {
|
|
74
|
+
const pattern = makePattern('no-eval', 'Avoid `eval`');
|
|
75
|
+
const largeContent = 'eval("bad")\n'.repeat(50000); // >500K
|
|
76
|
+
const files = { 'big.js': largeContent };
|
|
77
|
+
const report = (0, index_1.scanForDrift)(files, [pattern]);
|
|
78
|
+
(0, vitest_1.expect)(report.violations).toEqual([]);
|
|
79
|
+
});
|
|
80
|
+
(0, vitest_1.it)('calculates score as 1 - (violations * 0.1)', () => {
|
|
81
|
+
const pattern = makePattern('no-eval', 'Avoid `eval`');
|
|
82
|
+
const files = { 'a.js': 'eval(1)\neval(2)\neval(3)' };
|
|
83
|
+
const report = (0, index_1.scanForDrift)(files, [pattern]);
|
|
84
|
+
(0, vitest_1.expect)(report.violations).toHaveLength(3);
|
|
85
|
+
(0, vitest_1.expect)(report.score).toBeCloseTo(0.7);
|
|
86
|
+
});
|
|
87
|
+
(0, vitest_1.it)('clamps score at 0', () => {
|
|
88
|
+
const pattern = makePattern('no-eval', 'Avoid `eval`');
|
|
89
|
+
const lines = Array.from({ length: 15 }, () => 'eval("x")').join('\n');
|
|
90
|
+
const files = { 'a.js': lines };
|
|
91
|
+
const report = (0, index_1.scanForDrift)(files, [pattern]);
|
|
92
|
+
(0, vitest_1.expect)(report.score).toBe(0);
|
|
93
|
+
});
|
|
94
|
+
(0, vitest_1.it)('skips patterns with null antiPatterns', () => {
|
|
95
|
+
const pattern = makePattern('info-only', null);
|
|
96
|
+
const files = { 'app.js': 'eval("anything")' };
|
|
97
|
+
const report = (0, index_1.scanForDrift)(files, [pattern]);
|
|
98
|
+
(0, vitest_1.expect)(report.violations).toEqual([]);
|
|
99
|
+
});
|
|
100
|
+
(0, vitest_1.it)('tracks scannedFiles and scannedPatterns', () => {
|
|
101
|
+
const pattern = makePattern('test', 'Avoid `eval`');
|
|
102
|
+
const files = { 'a.js': 'ok', 'b.js': 'fine' };
|
|
103
|
+
const report = (0, index_1.scanForDrift)(files, [pattern]);
|
|
104
|
+
(0, vitest_1.expect)(report.scannedFiles).toBe(2);
|
|
105
|
+
(0, vitest_1.expect)(report.scannedPatterns).toBe(1);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=drift.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift.test.js","sourceRoot":"","sources":["../../src/__tests__/drift.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drift Scanner
|
|
3
|
+
*
|
|
4
|
+
* Detects state drift where actual codebase diverges from governance patterns.
|
|
5
|
+
* Scans file content against anti-pattern rules defined in blessed patterns.
|
|
6
|
+
*
|
|
7
|
+
* Extracted from Charter Cloud (ADR-001).
|
|
8
|
+
*
|
|
9
|
+
* Note: In the Cloud version, patterns are fetched from D1. In the Kit version,
|
|
10
|
+
* patterns are provided directly (loaded from .charter/patterns/ by the CLI).
|
|
11
|
+
*/
|
|
12
|
+
import type { Pattern, DriftReport } from '@stackbilt/types';
|
|
13
|
+
/**
|
|
14
|
+
* Scan files for governance violations (drift) against provided patterns.
|
|
15
|
+
*
|
|
16
|
+
* @param files - Map of filename to file content
|
|
17
|
+
* @param patterns - Active blessed-stack patterns to check against
|
|
18
|
+
* @returns Drift report with score and violations
|
|
19
|
+
*/
|
|
20
|
+
export declare function scanForDrift(files: Record<string, string>, patterns: Pattern[]): DriftReport;
|
|
21
|
+
/**
|
|
22
|
+
* Extract testable regexes from anti-pattern text.
|
|
23
|
+
*
|
|
24
|
+
* Supports:
|
|
25
|
+
* - Regex literals: /pattern/flags
|
|
26
|
+
* - Code tokens: `keyword`
|
|
27
|
+
*/
|
|
28
|
+
export declare function extractRules(antiPatternText: string): RegExp[];
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAkB,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAM7E;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,QAAQ,EAAE,OAAO,EAAE,GAClB,WAAW,CA0Cb;AAMD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,EAAE,CA4B9D"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Drift Scanner
|
|
4
|
+
*
|
|
5
|
+
* Detects state drift where actual codebase diverges from governance patterns.
|
|
6
|
+
* Scans file content against anti-pattern rules defined in blessed patterns.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from Charter Cloud (ADR-001).
|
|
9
|
+
*
|
|
10
|
+
* Note: In the Cloud version, patterns are fetched from D1. In the Kit version,
|
|
11
|
+
* patterns are provided directly (loaded from .charter/patterns/ by the CLI).
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.scanForDrift = scanForDrift;
|
|
15
|
+
exports.extractRules = extractRules;
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Public API
|
|
18
|
+
// ============================================================================
|
|
19
|
+
/**
|
|
20
|
+
* Scan files for governance violations (drift) against provided patterns.
|
|
21
|
+
*
|
|
22
|
+
* @param files - Map of filename to file content
|
|
23
|
+
* @param patterns - Active blessed-stack patterns to check against
|
|
24
|
+
* @returns Drift report with score and violations
|
|
25
|
+
*/
|
|
26
|
+
function scanForDrift(files, patterns) {
|
|
27
|
+
const violations = [];
|
|
28
|
+
let totalFiles = 0;
|
|
29
|
+
for (const [filename, content] of Object.entries(files)) {
|
|
30
|
+
totalFiles++;
|
|
31
|
+
// Skip binary or large files
|
|
32
|
+
if (content.length > 500000)
|
|
33
|
+
continue;
|
|
34
|
+
for (const pattern of patterns) {
|
|
35
|
+
if (pattern.antiPatterns) {
|
|
36
|
+
const rules = extractRules(pattern.antiPatterns);
|
|
37
|
+
for (const rule of rules) {
|
|
38
|
+
const lines = content.split('\n');
|
|
39
|
+
lines.forEach((line, index) => {
|
|
40
|
+
if (rule.test(line)) {
|
|
41
|
+
violations.push({
|
|
42
|
+
file: filename,
|
|
43
|
+
line: index + 1,
|
|
44
|
+
snippet: line.trim().substring(0, 100),
|
|
45
|
+
patternName: pattern.name,
|
|
46
|
+
antiPattern: pattern.antiPatterns,
|
|
47
|
+
severity: 'MAJOR'
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const score = Math.max(0, 1.0 - (violations.length * 0.1));
|
|
56
|
+
return {
|
|
57
|
+
score,
|
|
58
|
+
violations,
|
|
59
|
+
scannedFiles: totalFiles,
|
|
60
|
+
scannedPatterns: patterns.length,
|
|
61
|
+
timestamp: new Date().toISOString()
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// Rule Extraction
|
|
66
|
+
// ============================================================================
|
|
67
|
+
/**
|
|
68
|
+
* Extract testable regexes from anti-pattern text.
|
|
69
|
+
*
|
|
70
|
+
* Supports:
|
|
71
|
+
* - Regex literals: /pattern/flags
|
|
72
|
+
* - Code tokens: `keyword`
|
|
73
|
+
*/
|
|
74
|
+
function extractRules(antiPatternText) {
|
|
75
|
+
const rules = [];
|
|
76
|
+
// Look for regex-like strings: /regex/flags
|
|
77
|
+
const regexMatches = [...antiPatternText.matchAll(/\/([^\/]+)\/([gimsuy]*)/g)];
|
|
78
|
+
if (regexMatches.length > 0) {
|
|
79
|
+
regexMatches.forEach(match => {
|
|
80
|
+
const pattern = match[1];
|
|
81
|
+
const flags = match[2] || '';
|
|
82
|
+
try {
|
|
83
|
+
rules.push(new RegExp(pattern, flags));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Ignore invalid regex
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// Look for code-like tokens: `code`
|
|
91
|
+
const codeMatches = antiPatternText.match(/`([^`]+)`/g);
|
|
92
|
+
if (codeMatches) {
|
|
93
|
+
codeMatches.forEach(m => {
|
|
94
|
+
const keyword = m.slice(1, -1);
|
|
95
|
+
const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
96
|
+
rules.push(new RegExp(escaped));
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return rules;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stackbilt/drift",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Drift scanner — detects codebase divergence from governance patterns",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18.0.0"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/Stackbilt-dev/charter.git",
|
|
24
|
+
"directory": "packages/drift"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/Stackbilt-dev/charter/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/Stackbilt-dev/charter#readme",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@stackbilt/types": "workspace:*"
|
|
32
|
+
},
|
|
33
|
+
"license": "Apache-2.0"
|
|
34
|
+
}
|