@aiready/pattern-detect 0.11.4 → 0.11.6

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.
Files changed (41) hide show
  1. package/dist/__tests__/context-rules.test.d.ts +2 -0
  2. package/dist/__tests__/context-rules.test.d.ts.map +1 -0
  3. package/dist/__tests__/context-rules.test.js +189 -0
  4. package/dist/__tests__/context-rules.test.js.map +1 -0
  5. package/dist/__tests__/detector.test.d.ts +2 -0
  6. package/dist/__tests__/detector.test.d.ts.map +1 -0
  7. package/dist/__tests__/detector.test.js +259 -0
  8. package/dist/__tests__/detector.test.js.map +1 -0
  9. package/dist/__tests__/grouping.test.d.ts +2 -0
  10. package/dist/__tests__/grouping.test.d.ts.map +1 -0
  11. package/dist/__tests__/grouping.test.js +443 -0
  12. package/dist/__tests__/grouping.test.js.map +1 -0
  13. package/dist/__tests__/scoring.test.d.ts +2 -0
  14. package/dist/__tests__/scoring.test.d.ts.map +1 -0
  15. package/dist/__tests__/scoring.test.js +102 -0
  16. package/dist/__tests__/scoring.test.js.map +1 -0
  17. package/dist/cli.d.ts.map +1 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/context-rules.d.ts +41 -0
  20. package/dist/context-rules.d.ts.map +1 -0
  21. package/dist/context-rules.js +225 -0
  22. package/dist/context-rules.js.map +1 -0
  23. package/dist/detector.d.ts +40 -0
  24. package/dist/detector.d.ts.map +1 -0
  25. package/dist/detector.js +385 -0
  26. package/dist/detector.js.map +1 -0
  27. package/dist/extractors/python-extractor.d.ts +19 -0
  28. package/dist/extractors/python-extractor.d.ts.map +1 -0
  29. package/dist/extractors/python-extractor.js +164 -0
  30. package/dist/extractors/python-extractor.js.map +1 -0
  31. package/dist/grouping.d.ts +54 -0
  32. package/dist/grouping.d.ts.map +1 -0
  33. package/dist/grouping.js +347 -0
  34. package/dist/grouping.js.map +1 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/scoring.d.ts +12 -0
  38. package/dist/scoring.d.ts.map +1 -0
  39. package/dist/scoring.js +116 -0
  40. package/dist/scoring.js.map +1 -0
  41. package/package.json +2 -2
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=context-rules.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-rules.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/context-rules.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,189 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { calculateSeverity, filterBySeverity, getSeverityLabel } from '../context-rules';
3
+ describe('Context-Aware Severity', () => {
4
+ describe('Test Fixture Detection', () => {
5
+ it('should mark test fixtures as info severity', () => {
6
+ const file = 'src/__tests__/user.test.ts';
7
+ const code = `
8
+ beforeAll(async () => {
9
+ await setupDatabase();
10
+ });
11
+
12
+ afterAll(async () => {
13
+ await cleanupDatabase();
14
+ });
15
+ `;
16
+ const result = calculateSeverity(file, file, code, 1.0, 10);
17
+ expect(result.severity).toBe('info');
18
+ expect(result.reason).toContain('test isolation');
19
+ expect(result.matchedRule).toBe('test-fixtures');
20
+ });
21
+ it('should detect test files with spec extension', () => {
22
+ const file = 'src/components/Button.spec.ts';
23
+ const code = `
24
+ beforeEach(() => {
25
+ jest.clearAllMocks();
26
+ });
27
+ `;
28
+ const result = calculateSeverity(file, file, code, 0.9, 8);
29
+ expect(result.severity).toBe('info');
30
+ expect(result.matchedRule).toBe('test-fixtures');
31
+ });
32
+ });
33
+ describe('Template Detection', () => {
34
+ it('should mark email templates as low severity', () => {
35
+ const file = 'src/email-templates/payment-failed.ts';
36
+ const code = `
37
+ export function getEmailTemplate() {
38
+ return {
39
+ subject: 'Payment Failed',
40
+ html: '<div>Your payment has failed</div>',
41
+ body: 'Payment failed'
42
+ };
43
+ }
44
+ `;
45
+ const result = calculateSeverity(file, file, code, 0.8, 20);
46
+ expect(result.severity).toBe('minor');
47
+ expect(result.reason).toContain('branding consistency');
48
+ expect(result.matchedRule).toBe('templates');
49
+ });
50
+ });
51
+ describe('E2E Test Detection', () => {
52
+ it('should mark E2E page objects as low severity', () => {
53
+ const file = 'e2e/pages/login-page.ts';
54
+ const code = `
55
+ async function clickLoginButton() {
56
+ await page.click('button[type="submit"]');
57
+ }
58
+
59
+ async function fillUsername(value: string) {
60
+ await page.fill('input[name="username"]', value);
61
+ }
62
+ `;
63
+ const result = calculateSeverity(file, file, code, 0.85, 15);
64
+ // Debug: The detection is working now with 'await page' pattern
65
+ expect(result.severity).toBe('minor');
66
+ expect(result.reason).toContain('test independence');
67
+ expect(result.matchedRule).toBe('e2e-page-objects');
68
+ });
69
+ });
70
+ describe('Configuration Files', () => {
71
+ it('should mark config files as low severity', () => {
72
+ const file = 'packages/web/jest.config.ts';
73
+ const code = `
74
+ export default {
75
+ preset: 'ts-jest',
76
+ testEnvironment: 'node'
77
+ };
78
+ `;
79
+ const result = calculateSeverity(file, file, code, 0.9, 10);
80
+ expect(result.severity).toBe('minor');
81
+ expect(result.matchedRule).toBe('config-files');
82
+ });
83
+ });
84
+ describe('Type Definitions', () => {
85
+ it('should mark type definitions as info severity', () => {
86
+ const file = 'src/types/user.d.ts';
87
+ const code = `
88
+ interface User {
89
+ id: string;
90
+ name: string;
91
+ email: string;
92
+ }
93
+
94
+ type UserRole = 'admin' | 'user';
95
+ `;
96
+ const result = calculateSeverity(file, file, code, 0.95, 12);
97
+ expect(result.severity).toBe('info');
98
+ expect(result.reason).toContain('type safety');
99
+ expect(result.matchedRule).toBe('type-definitions');
100
+ });
101
+ });
102
+ describe('Critical Duplication', () => {
103
+ it('should mark large identical code as critical', () => {
104
+ const file = 'src/utils/helper.ts';
105
+ const code = 'function compute() {\n'.repeat(35) + '}';
106
+ const result = calculateSeverity(file, file, code, 1.0, 35);
107
+ expect(result.severity).toBe('critical');
108
+ expect(result.reason).toContain('maintenance burden');
109
+ });
110
+ it('should mark high similarity medium-sized code as high', () => {
111
+ const file = 'src/services/api.ts';
112
+ const code = 'function fetchData() {\n'.repeat(20) + '}';
113
+ const result = calculateSeverity(file, file, code, 0.96, 20);
114
+ expect(result.severity).toBe('major');
115
+ expect(result.reason).toContain('consolidated');
116
+ });
117
+ });
118
+ describe('Severity Filtering', () => {
119
+ it('should filter by minimum severity', () => {
120
+ const duplicates = [
121
+ { severity: 'critical' },
122
+ { severity: 'major' },
123
+ { severity: 'minor' },
124
+ { severity: 'info' },
125
+ ];
126
+ const filtered = filterBySeverity(duplicates, 'minor');
127
+ expect(filtered).toHaveLength(3);
128
+ expect(filtered.map(d => d.severity)).toEqual(['critical', 'major', 'minor']);
129
+ });
130
+ it('should show all severities when filtering by info', () => {
131
+ const duplicates = [
132
+ { severity: 'critical' },
133
+ { severity: 'info' },
134
+ ];
135
+ const filtered = filterBySeverity(duplicates, 'info');
136
+ expect(filtered).toHaveLength(2);
137
+ });
138
+ it('should only show critical when filtering by critical', () => {
139
+ const duplicates = [
140
+ { severity: 'critical' },
141
+ { severity: 'major' },
142
+ { severity: 'minor' },
143
+ ];
144
+ const filtered = filterBySeverity(duplicates, 'critical');
145
+ expect(filtered).toHaveLength(1);
146
+ expect(filtered[0].severity).toBe('critical');
147
+ });
148
+ });
149
+ describe('Severity Labels', () => {
150
+ it('should return correct labels with emojis', () => {
151
+ expect(getSeverityLabel('critical')).toContain('CRITICAL');
152
+ expect(getSeverityLabel('major')).toContain('MAJOR');
153
+ expect(getSeverityLabel('minor')).toContain('MINOR');
154
+ expect(getSeverityLabel('info')).toContain('INFO');
155
+ });
156
+ });
157
+ describe('Mock Data Detection', () => {
158
+ it('should mark mock data as info severity', () => {
159
+ const file = 'src/__mocks__/user-data.ts';
160
+ const code = `
161
+ export const mockUser = {
162
+ id: '123',
163
+ name: 'Test User',
164
+ email: 'test@example.com'
165
+ };
166
+ `;
167
+ const result = calculateSeverity(file, file, code, 0.9, 10);
168
+ expect(result.severity).toBe('info');
169
+ expect(result.matchedRule).toBe('mock-data');
170
+ });
171
+ });
172
+ describe('Migration Scripts Detection', () => {
173
+ it('should mark migration scripts as info severity', () => {
174
+ const file = 'db/migrations/001_create_users.ts';
175
+ const code = `
176
+ export async function up(db: Database) {
177
+ await db.createTable('users', {
178
+ id: 'uuid',
179
+ name: 'text'
180
+ });
181
+ }
182
+ `;
183
+ const result = calculateSeverity(file, file, code, 0.95, 15);
184
+ expect(result.severity).toBe('info');
185
+ expect(result.matchedRule).toBe('migration-scripts');
186
+ });
187
+ });
188
+ });
189
+ //# sourceMappingURL=context-rules.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-rules.test.js","sourceRoot":"","sources":["../../src/__tests__/context-rules.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=detector.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/detector.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,259 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { detectDuplicatePatterns } from '../detector';
3
+ describe('detectDuplicatePatterns', () => {
4
+ it('should detect exact duplicate functions', async () => {
5
+ const files = [
6
+ {
7
+ file: 'file1.ts',
8
+ content: `
9
+ async function getUserData(id: string) {
10
+ const user = await db.users.findOne({ id });
11
+ if (!user) {
12
+ throw new Error('Not found');
13
+ }
14
+ return user;
15
+ }
16
+ `,
17
+ },
18
+ {
19
+ file: 'file2.ts',
20
+ content: `
21
+ async function getUserInfo(userId: string) {
22
+ const user = await db.users.findOne({ id: userId });
23
+ if (!user) {
24
+ throw new Error('Not found');
25
+ }
26
+ return user;
27
+ }
28
+ `,
29
+ },
30
+ ];
31
+ const duplicates = await detectDuplicatePatterns(files, {
32
+ minSimilarity: 0.6,
33
+ minLines: 5,
34
+ approx: false, // Disable approximation for test reliability
35
+ });
36
+ expect(duplicates.length).toBeGreaterThan(0);
37
+ expect(duplicates[0].similarity).toBeGreaterThan(0.6);
38
+ });
39
+ it('should detect similar but not identical functions', async () => {
40
+ const files = [
41
+ {
42
+ file: 'file1.ts',
43
+ content: `
44
+ async function getUserData(id: string) {
45
+ const user = await database.users.findOne({ id: id });
46
+ if (!user) {
47
+ throw new Error('User not found');
48
+ }
49
+ return user;
50
+ }
51
+ `,
52
+ },
53
+ {
54
+ file: 'file2.ts',
55
+ content: `
56
+ async function getUserData(userId: string) {
57
+ const user = await database.users.findOne({ id: userId });
58
+ if (!user) {
59
+ throw new Error('User not found');
60
+ }
61
+ return user;
62
+ }
63
+ `,
64
+ },
65
+ ];
66
+ const duplicates = await detectDuplicatePatterns(files, {
67
+ minSimilarity: 0.6,
68
+ minLines: 5,
69
+ approx: false, // Disable approximation for test reliability
70
+ });
71
+ expect(duplicates.length).toBeGreaterThan(0);
72
+ });
73
+ it('should categorize API handler patterns', async () => {
74
+ const files = [
75
+ {
76
+ file: 'file1.ts',
77
+ content: `
78
+ app.get('/api/users/:id', async (request, response) => {
79
+ const user = await db.users.findOne({ id: request.params.id });
80
+ response.json(user);
81
+ });
82
+ `,
83
+ },
84
+ {
85
+ file: 'file2.ts',
86
+ content: `
87
+ app.get('/api/posts/:id', async (req, res) => {
88
+ const post = await db.posts.findOne({ id: req.params.id });
89
+ res.json(post);
90
+ });
91
+ `,
92
+ },
93
+ ];
94
+ const duplicates = await detectDuplicatePatterns(files, {
95
+ minSimilarity: 0.4,
96
+ minLines: 3,
97
+ approx: false,
98
+ });
99
+ expect(duplicates.length).toBeGreaterThan(0);
100
+ expect(duplicates[0].patternType).toBe('api-handler');
101
+ });
102
+ it('should categorize validator patterns', async () => {
103
+ const files = [
104
+ {
105
+ file: 'file1.ts',
106
+ content: `function validateEmail(email: string) {
107
+ if (!email) {
108
+ throw new Error('Email is required');
109
+ }
110
+ if (!email.includes('@')) {
111
+ throw new Error('Invalid email format');
112
+ }
113
+ return true;
114
+ }
115
+ `,
116
+ },
117
+ {
118
+ file: 'file2.ts',
119
+ content: `function validateUsername(username: string) {
120
+ if (!username) {
121
+ throw new Error('Username is required');
122
+ }
123
+ if (username.length < 3) {
124
+ throw new Error('Username too short');
125
+ }
126
+ return true;
127
+ }
128
+ `,
129
+ },
130
+ ];
131
+ const duplicates = await detectDuplicatePatterns(files, {
132
+ minSimilarity: 0.2,
133
+ minLines: 3,
134
+ approx: false,
135
+ });
136
+ expect(duplicates.length).toBeGreaterThan(0);
137
+ expect(duplicates[0].patternType).toBe('validator');
138
+ });
139
+ it('should calculate token cost', async () => {
140
+ const files = [
141
+ {
142
+ file: 'file1.ts',
143
+ content: `
144
+ function processData(data: any) {
145
+ const validated = validateInput(data);
146
+ const result = validated.map((item: any) => item.value);
147
+ const filtered = result.filter((x: any) => x !== null);
148
+ return filtered;
149
+ }
150
+ `,
151
+ },
152
+ {
153
+ file: 'file2.ts',
154
+ content: `
155
+ function processData(items: any) {
156
+ const validated = validateInput(items);
157
+ const result = validated.map((element: any) => element.value);
158
+ const filtered = result.filter((x: any) => x !== null);
159
+ return filtered;
160
+ }
161
+ `,
162
+ },
163
+ ];
164
+ const duplicates = await detectDuplicatePatterns(files, {
165
+ minSimilarity: 0.5,
166
+ minLines: 3,
167
+ approx: false,
168
+ });
169
+ expect(duplicates.length).toBeGreaterThan(0);
170
+ expect(duplicates[0].tokenCost).toBeGreaterThan(0);
171
+ });
172
+ it('should not detect patterns below similarity threshold', async () => {
173
+ const files = [
174
+ {
175
+ file: 'file1.ts',
176
+ content: `
177
+ function complexFunction(param: string) {
178
+ const result = someComplexLogic(param);
179
+ const transformed = anotherTransform(result);
180
+ return transformed;
181
+ }
182
+ `,
183
+ },
184
+ {
185
+ file: 'file2.ts',
186
+ content: `
187
+ function totallyDifferent() {
188
+ return 42;
189
+ }
190
+ `,
191
+ },
192
+ ];
193
+ const duplicates = await detectDuplicatePatterns(files, {
194
+ minSimilarity: 0.9,
195
+ minLines: 3,
196
+ });
197
+ expect(duplicates.length).toBe(0);
198
+ });
199
+ it('should not compare blocks from the same file', async () => {
200
+ const files = [
201
+ {
202
+ file: 'file1.ts',
203
+ content: `
204
+ function func1() {
205
+ return 1;
206
+ }
207
+
208
+ function func2() {
209
+ return 2;
210
+ }
211
+ `,
212
+ },
213
+ ];
214
+ const duplicates = await detectDuplicatePatterns(files, {
215
+ minSimilarity: 0.5,
216
+ minLines: 2,
217
+ });
218
+ expect(duplicates.length).toBe(0);
219
+ });
220
+ it('should sort duplicates by similarity and token cost', async () => {
221
+ const files = [
222
+ {
223
+ file: 'file1.ts',
224
+ content: `
225
+ function a() {
226
+ const x = 1;
227
+ return x;
228
+ }
229
+ `,
230
+ },
231
+ {
232
+ file: 'file2.ts',
233
+ content: `
234
+ function b() {
235
+ const x = 1;
236
+ return x;
237
+ }
238
+ `,
239
+ },
240
+ {
241
+ file: 'file3.ts',
242
+ content: `
243
+ function c() {
244
+ const y = 2;
245
+ return y;
246
+ }
247
+ `,
248
+ },
249
+ ];
250
+ const duplicates = await detectDuplicatePatterns(files, {
251
+ minSimilarity: 0.7,
252
+ minLines: 2,
253
+ });
254
+ if (duplicates.length > 1) {
255
+ expect(duplicates[0].similarity).toBeGreaterThanOrEqual(duplicates[1].similarity);
256
+ }
257
+ });
258
+ });
259
+ //# sourceMappingURL=detector.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector.test.js","sourceRoot":"","sources":["../../src/__tests__/detector.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=grouping.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grouping.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/grouping.test.ts"],"names":[],"mappings":""}