@rigour-labs/core 2.22.0 → 3.0.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.
Files changed (117) hide show
  1. package/README.md +58 -0
  2. package/dist/context.test.js +2 -3
  3. package/dist/environment.test.js +2 -1
  4. package/dist/gates/agent-team.d.ts +2 -1
  5. package/dist/gates/agent-team.js +1 -0
  6. package/dist/gates/base.d.ts +3 -1
  7. package/dist/gates/base.js +3 -0
  8. package/dist/gates/checkpoint.d.ts +2 -1
  9. package/dist/gates/checkpoint.js +3 -2
  10. package/dist/gates/context-window-artifacts.d.ts +2 -1
  11. package/dist/gates/context-window-artifacts.js +6 -3
  12. package/dist/gates/context.d.ts +2 -1
  13. package/dist/gates/context.js +1 -0
  14. package/dist/gates/coverage.js +3 -1
  15. package/dist/gates/dependency.js +5 -5
  16. package/dist/gates/duplication-drift.d.ts +2 -1
  17. package/dist/gates/duplication-drift.js +4 -1
  18. package/dist/gates/environment.js +4 -4
  19. package/dist/gates/hallucinated-imports.d.ts +21 -2
  20. package/dist/gates/hallucinated-imports.js +116 -2
  21. package/dist/gates/inconsistent-error-handling.d.ts +2 -1
  22. package/dist/gates/inconsistent-error-handling.js +21 -7
  23. package/dist/gates/promise-safety.d.ts +68 -0
  24. package/dist/gates/promise-safety.js +509 -0
  25. package/dist/gates/retry-loop-breaker.d.ts +2 -1
  26. package/dist/gates/retry-loop-breaker.js +2 -1
  27. package/dist/gates/runner.js +34 -1
  28. package/dist/gates/safety.d.ts +2 -1
  29. package/dist/gates/safety.js +2 -1
  30. package/dist/gates/security-patterns-owasp.test.d.ts +1 -0
  31. package/dist/gates/security-patterns-owasp.test.js +171 -0
  32. package/dist/gates/security-patterns.d.ts +6 -1
  33. package/dist/gates/security-patterns.js +101 -0
  34. package/dist/gates/structure.js +1 -1
  35. package/dist/hooks/checker.d.ts +23 -0
  36. package/dist/hooks/checker.js +222 -0
  37. package/dist/hooks/checker.test.d.ts +1 -0
  38. package/dist/hooks/checker.test.js +132 -0
  39. package/dist/hooks/index.d.ts +9 -0
  40. package/dist/hooks/index.js +8 -0
  41. package/dist/hooks/standalone-checker.d.ts +15 -0
  42. package/dist/hooks/standalone-checker.js +106 -0
  43. package/dist/hooks/templates.d.ts +22 -0
  44. package/dist/hooks/templates.js +232 -0
  45. package/dist/hooks/types.d.ts +34 -0
  46. package/dist/hooks/types.js +21 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.js +2 -0
  49. package/dist/services/fix-packet-service.d.ts +0 -1
  50. package/dist/services/fix-packet-service.js +9 -14
  51. package/dist/services/score-history.d.ts +54 -0
  52. package/dist/services/score-history.js +122 -0
  53. package/dist/templates/index.js +176 -0
  54. package/dist/types/fix-packet.d.ts +5 -5
  55. package/dist/types/fix-packet.js +1 -1
  56. package/dist/types/index.d.ts +207 -0
  57. package/dist/types/index.js +32 -0
  58. package/package.json +21 -1
  59. package/src/context.test.ts +0 -256
  60. package/src/discovery.test.ts +0 -88
  61. package/src/discovery.ts +0 -112
  62. package/src/environment.test.ts +0 -115
  63. package/src/gates/agent-team.test.ts +0 -134
  64. package/src/gates/agent-team.ts +0 -210
  65. package/src/gates/ast-handlers/base.ts +0 -13
  66. package/src/gates/ast-handlers/python.ts +0 -145
  67. package/src/gates/ast-handlers/python_parser.py +0 -181
  68. package/src/gates/ast-handlers/typescript.ts +0 -264
  69. package/src/gates/ast-handlers/universal.ts +0 -184
  70. package/src/gates/ast.ts +0 -54
  71. package/src/gates/base.ts +0 -28
  72. package/src/gates/checkpoint.test.ts +0 -135
  73. package/src/gates/checkpoint.ts +0 -311
  74. package/src/gates/content.ts +0 -51
  75. package/src/gates/context-window-artifacts.ts +0 -277
  76. package/src/gates/context.ts +0 -270
  77. package/src/gates/coverage.ts +0 -74
  78. package/src/gates/dependency.ts +0 -108
  79. package/src/gates/duplication-drift.ts +0 -231
  80. package/src/gates/environment.ts +0 -94
  81. package/src/gates/file.ts +0 -46
  82. package/src/gates/hallucinated-imports.ts +0 -361
  83. package/src/gates/inconsistent-error-handling.ts +0 -254
  84. package/src/gates/retry-loop-breaker.ts +0 -151
  85. package/src/gates/runner.ts +0 -188
  86. package/src/gates/safety.ts +0 -56
  87. package/src/gates/security-patterns.test.ts +0 -162
  88. package/src/gates/security-patterns.ts +0 -306
  89. package/src/gates/structure.ts +0 -36
  90. package/src/index.ts +0 -13
  91. package/src/pattern-index/embeddings.ts +0 -84
  92. package/src/pattern-index/index.ts +0 -59
  93. package/src/pattern-index/indexer.test.ts +0 -276
  94. package/src/pattern-index/indexer.ts +0 -1023
  95. package/src/pattern-index/matcher.test.ts +0 -293
  96. package/src/pattern-index/matcher.ts +0 -493
  97. package/src/pattern-index/overrides.ts +0 -235
  98. package/src/pattern-index/security.ts +0 -151
  99. package/src/pattern-index/staleness.test.ts +0 -313
  100. package/src/pattern-index/staleness.ts +0 -568
  101. package/src/pattern-index/types.ts +0 -339
  102. package/src/safety.test.ts +0 -53
  103. package/src/services/adaptive-thresholds.test.ts +0 -189
  104. package/src/services/adaptive-thresholds.ts +0 -275
  105. package/src/services/context-engine.ts +0 -104
  106. package/src/services/fix-packet-service.ts +0 -42
  107. package/src/services/state-service.ts +0 -138
  108. package/src/smoke.test.ts +0 -18
  109. package/src/templates/index.ts +0 -338
  110. package/src/types/fix-packet.ts +0 -32
  111. package/src/types/index.ts +0 -200
  112. package/src/utils/logger.ts +0 -43
  113. package/src/utils/scanner.test.ts +0 -37
  114. package/src/utils/scanner.ts +0 -43
  115. package/tsconfig.json +0 -10
  116. package/vitest.config.ts +0 -7
  117. package/vitest.setup.ts +0 -30
@@ -1,568 +0,0 @@
1
- /**
2
- * Staleness Detector
3
- *
4
- * Detects when AI is suggesting deprecated or outdated patterns.
5
- * Uses package.json analysis and a deprecation database.
6
- */
7
-
8
- import * as fs from 'fs/promises';
9
- import * as path from 'path';
10
- import semver from 'semver';
11
- import type {
12
- StalenessResult,
13
- StalenessIssue,
14
- DeprecationEntry
15
- } from './types.js';
16
-
17
- /**
18
- * Built-in deprecation database.
19
- * This is bundled with Rigour and updated with releases.
20
- */
21
- const BUILT_IN_DEPRECATIONS: DeprecationEntry[] = [
22
- // React deprecations
23
- {
24
- pattern: 'componentWillMount',
25
- library: 'react',
26
- deprecatedIn: '16.3.0',
27
- replacement: 'useEffect(() => { ... }, [])',
28
- severity: 'error',
29
- reason: 'Unsafe lifecycle method removed in React 18',
30
- docs: 'https://react.dev/reference/react/Component#unsafe_componentwillmount'
31
- },
32
- {
33
- pattern: 'componentWillReceiveProps',
34
- library: 'react',
35
- deprecatedIn: '16.3.0',
36
- replacement: 'getDerivedStateFromProps or useEffect',
37
- severity: 'error',
38
- reason: 'Unsafe lifecycle method removed in React 18'
39
- },
40
- {
41
- pattern: 'componentWillUpdate',
42
- library: 'react',
43
- deprecatedIn: '16.3.0',
44
- replacement: 'getSnapshotBeforeUpdate or useEffect',
45
- severity: 'error',
46
- reason: 'Unsafe lifecycle method removed in React 18'
47
- },
48
- {
49
- pattern: 'UNSAFE_componentWillMount',
50
- library: 'react',
51
- deprecatedIn: '18.0.0',
52
- replacement: 'useEffect(() => { ... }, [])',
53
- severity: 'warning',
54
- reason: 'Prepare for React 19 removal'
55
- },
56
- {
57
- pattern: 'ReactDOM.render',
58
- library: 'react-dom',
59
- deprecatedIn: '18.0.0',
60
- replacement: 'createRoot(container).render(<App />)',
61
- severity: 'error',
62
- reason: 'Legacy root API deprecated in React 18'
63
- },
64
- {
65
- pattern: 'ReactDOM.hydrate',
66
- library: 'react-dom',
67
- deprecatedIn: '18.0.0',
68
- replacement: 'hydrateRoot(container, <App />)',
69
- severity: 'error',
70
- reason: 'Legacy hydration API deprecated in React 18'
71
- },
72
-
73
- // Package deprecations
74
- {
75
- pattern: "import.*from ['\"]moment['\"]",
76
- deprecatedIn: 'ecosystem',
77
- replacement: "import { format } from 'date-fns'",
78
- severity: 'warning',
79
- reason: 'moment.js is in maintenance mode since September 2020',
80
- docs: 'https://momentjs.com/docs/#/-project-status/'
81
- },
82
- {
83
- pattern: "require\\(['\"]request['\"]\\)",
84
- deprecatedIn: 'ecosystem',
85
- replacement: 'Use native fetch or axios',
86
- severity: 'error',
87
- reason: 'request package deprecated in February 2020'
88
- },
89
- {
90
- pattern: "import.*from ['\"]request['\"]",
91
- deprecatedIn: 'ecosystem',
92
- replacement: 'Use native fetch or axios',
93
- severity: 'error',
94
- reason: 'request package deprecated in February 2020'
95
- },
96
-
97
- // JavaScript/TypeScript deprecations
98
- {
99
- pattern: '\\bvar\\s+\\w+\\s*=',
100
- deprecatedIn: 'es6',
101
- replacement: 'Use const or let',
102
- severity: 'warning',
103
- reason: 'var has function scope which leads to bugs. Use block-scoped const/let'
104
- },
105
-
106
- // Redux deprecations
107
- {
108
- pattern: 'createStore\\(',
109
- library: 'redux',
110
- deprecatedIn: '4.2.0',
111
- replacement: "configureStore from '@reduxjs/toolkit'",
112
- severity: 'warning',
113
- reason: 'Redux Toolkit is now the recommended way',
114
- docs: 'https://redux.js.org/introduction/why-rtk-is-redux-today'
115
- },
116
-
117
- // Node.js deprecations
118
- {
119
- pattern: 'new Buffer\\(',
120
- deprecatedIn: 'node@6.0.0',
121
- replacement: 'Buffer.alloc() or Buffer.from()',
122
- severity: 'error',
123
- reason: 'Buffer constructor is a security hazard'
124
- },
125
-
126
- // Express deprecations
127
- {
128
- pattern: 'app\\.del\\(',
129
- library: 'express',
130
- deprecatedIn: '4.0.0',
131
- replacement: 'app.delete()',
132
- severity: 'warning',
133
- reason: 'app.del() was renamed to app.delete()'
134
- },
135
-
136
- // TypeScript patterns to avoid
137
- {
138
- pattern: '\\benum\\s+\\w+',
139
- deprecatedIn: 'best-practice',
140
- replacement: 'const object with as const assertion',
141
- severity: 'info',
142
- reason: 'Enums have quirks. Consider using const objects for better tree-shaking',
143
- docs: 'https://www.typescriptlang.org/docs/handbook/enums.html#const-enums'
144
- },
145
-
146
- // Next.js deprecations
147
- {
148
- pattern: 'getInitialProps',
149
- library: 'next',
150
- deprecatedIn: '13.0.0',
151
- replacement: 'getServerSideProps or App Router with async components',
152
- severity: 'warning',
153
- reason: 'getInitialProps prevents static optimization'
154
- },
155
- {
156
- pattern: "from ['\"]next/router['\"]",
157
- library: 'next',
158
- deprecatedIn: '13.0.0',
159
- replacement: "useRouter from 'next/navigation' in App Router",
160
- severity: 'info',
161
- reason: 'Use next/navigation for App Router projects'
162
- },
163
-
164
- // ============================================================
165
- // SECURITY PATTERNS - Cross-language security vulnerabilities
166
- // ============================================================
167
-
168
- // Python CSRF disabled
169
- {
170
- pattern: 'csrf\\s*=\\s*False',
171
- deprecatedIn: 'security',
172
- replacement: "Never disable CSRF protection. Remove 'csrf = False' and use proper CSRF tokens.",
173
- severity: 'error',
174
- reason: 'CSRF protection is critical for security. Disabling it exposes users to cross-site request forgery attacks.'
175
- },
176
- {
177
- pattern: 'WTF_CSRF_ENABLED\\s*=\\s*False',
178
- deprecatedIn: 'security',
179
- replacement: "Never disable CSRF. Remove 'WTF_CSRF_ENABLED = False' from config.",
180
- severity: 'error',
181
- reason: 'Flask-WTF CSRF protection should never be disabled in production.'
182
- },
183
- {
184
- pattern: "@csrf_exempt",
185
- deprecatedIn: 'security',
186
- replacement: "Remove @csrf_exempt decorator. Use proper CSRF token handling instead.",
187
- severity: 'error',
188
- reason: 'csrf_exempt bypasses CSRF protection, creating security vulnerabilities.'
189
- },
190
-
191
- // Python hardcoded secrets
192
- {
193
- pattern: "SECRET_KEY\\s*=\\s*['\"][^'\"]{1,50}['\"]",
194
- deprecatedIn: 'security',
195
- replacement: "Use os.environ.get('SECRET_KEY') or secrets.token_hex(32)",
196
- severity: 'error',
197
- reason: 'Hardcoded secrets are exposed in version control and logs. Use environment variables.'
198
- },
199
- {
200
- pattern: "API_KEY\\s*=\\s*['\"][^'\"]+['\"]",
201
- deprecatedIn: 'security',
202
- replacement: "Use os.environ.get('API_KEY') for API credentials",
203
- severity: 'error',
204
- reason: 'Hardcoded API keys are a security risk. Use environment variables.'
205
- },
206
- {
207
- pattern: "PASSWORD\\s*=\\s*['\"][^'\"]+['\"]",
208
- deprecatedIn: 'security',
209
- replacement: "Never hardcode passwords. Use environment variables or secret managers.",
210
- severity: 'error',
211
- reason: 'Hardcoded passwords are a critical security vulnerability.'
212
- },
213
-
214
- // JavaScript/TypeScript prototype pollution
215
- {
216
- pattern: '\\.__proto__',
217
- deprecatedIn: 'security',
218
- replacement: "Use Object.getPrototypeOf() or Object.setPrototypeOf() instead of __proto__",
219
- severity: 'error',
220
- reason: 'Direct __proto__ access enables prototype pollution attacks.'
221
- },
222
- {
223
- pattern: '\\[\\s*[\'"]__proto__[\'"]\\s*\\]',
224
- deprecatedIn: 'security',
225
- replacement: "Never allow user input to access __proto__. Validate and sanitize object keys.",
226
- severity: 'error',
227
- reason: 'Bracket notation access to __proto__ is a prototype pollution vector.'
228
- },
229
- {
230
- pattern: '\\[\\s*[\'"]constructor[\'"]\\s*\\]\\s*\\[',
231
- deprecatedIn: 'security',
232
- replacement: "Block access to constructor property from user input.",
233
- severity: 'error',
234
- reason: 'constructor[constructor] pattern enables prototype pollution.'
235
- },
236
-
237
- // SQL Injection patterns
238
- {
239
- pattern: 'cursor\\.execute\\s*\\(\\s*f[\'"]',
240
- deprecatedIn: 'security',
241
- replacement: "Use parameterized queries: cursor.execute('SELECT * FROM users WHERE id = %s', (user_id,))",
242
- severity: 'error',
243
- reason: 'F-string SQL queries are vulnerable to SQL injection attacks.'
244
- },
245
- {
246
- pattern: '\\.execute\\s*\\([^)]*\\+[^)]*\\)',
247
- deprecatedIn: 'security',
248
- replacement: "Use parameterized queries instead of string concatenation.",
249
- severity: 'error',
250
- reason: 'String concatenation in SQL queries enables SQL injection.'
251
- },
252
-
253
- // XSS patterns
254
- {
255
- pattern: 'dangerouslySetInnerHTML',
256
- deprecatedIn: 'security',
257
- replacement: "Sanitize HTML with DOMPurify before using dangerouslySetInnerHTML, or use safe alternatives.",
258
- severity: 'warning',
259
- reason: 'dangerouslySetInnerHTML can lead to XSS vulnerabilities if content is not sanitized.'
260
- },
261
- {
262
- pattern: '\\.innerHTML\\s*=',
263
- deprecatedIn: 'security',
264
- replacement: "Use textContent for text, or sanitize HTML before setting innerHTML.",
265
- severity: 'warning',
266
- reason: 'Direct innerHTML assignment can lead to XSS attacks.'
267
- },
268
-
269
- // Insecure session/cookie settings
270
- {
271
- pattern: 'SESSION_COOKIE_SECURE\\s*=\\s*False',
272
- deprecatedIn: 'security',
273
- replacement: "Set SESSION_COOKIE_SECURE = True in production",
274
- severity: 'error',
275
- reason: 'Insecure cookies can be intercepted over HTTP connections.'
276
- },
277
- {
278
- pattern: 'SESSION_COOKIE_HTTPONLY\\s*=\\s*False',
279
- deprecatedIn: 'security',
280
- replacement: "Set SESSION_COOKIE_HTTPONLY = True to prevent XSS cookie theft",
281
- severity: 'error',
282
- reason: 'Non-HTTPOnly cookies are accessible via JavaScript, enabling XSS attacks.'
283
- },
284
-
285
- // Debug mode in production
286
- {
287
- pattern: 'DEBUG\\s*=\\s*True',
288
- deprecatedIn: 'security',
289
- replacement: "Use DEBUG = os.environ.get('DEBUG', 'False') == 'True'",
290
- severity: 'warning',
291
- reason: 'Debug mode in production exposes sensitive information and stack traces.'
292
- }
293
- ];
294
-
295
- /**
296
- * Project context extracted from package.json and other config files.
297
- */
298
- interface ProjectContext {
299
- dependencies: Record<string, string>;
300
- devDependencies: Record<string, string>;
301
- nodeVersion?: string;
302
- typescriptVersion?: string;
303
- }
304
-
305
- /**
306
- * Staleness Detector class.
307
- */
308
- export class StalenessDetector {
309
- private deprecations: DeprecationEntry[];
310
- private projectContext: ProjectContext | null = null;
311
- private rootDir: string;
312
- private remoteRulesUrl = 'https://raw.githubusercontent.com/rigour-labs/rules/main/deprecations.json';
313
-
314
- constructor(rootDir: string, customDeprecations: DeprecationEntry[] = []) {
315
- this.rootDir = rootDir;
316
- this.deprecations = [...BUILT_IN_DEPRECATIONS, ...customDeprecations];
317
- }
318
-
319
- /**
320
- * Fetch latest deprecation rules from Rigour's remote registry.
321
- * This ensures the tool stays up-to-date even without a package update.
322
- */
323
- async syncRemoteRules(): Promise<number> {
324
- try {
325
- // Using dynamic import for fetch to avoid Node < 18 issues
326
- const response = await fetch(this.remoteRulesUrl);
327
- if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
328
-
329
- const data = await response.json();
330
- if (data.deprecations && Array.isArray(data.deprecations)) {
331
- // Merge remote rules, avoiding duplicates
332
- const existingPatterns = new Set(this.deprecations.map(d => d.pattern));
333
- const newRules = data.deprecations.filter((d: DeprecationEntry) => !existingPatterns.has(d.pattern));
334
-
335
- this.deprecations.push(...newRules);
336
- return newRules.length;
337
- }
338
- return 0;
339
- } catch (error) {
340
- console.warn('Failed to sync remote rules, using built-in database:', error);
341
- return 0;
342
- }
343
- }
344
-
345
- /**
346
- * Check NPM registry for live deprecation status of project dependencies.
347
- * This is the ultimate "up-to-date" check.
348
- */
349
- async checkLiveRegistry(context: ProjectContext): Promise<StalenessIssue[]> {
350
- const issues: StalenessIssue[] = [];
351
- const { execa } = await import('execa');
352
-
353
- // We only check top-level dependencies to avoid noise/performance hits
354
- const deps = Object.keys(context.dependencies);
355
-
356
- for (const dep of deps) {
357
- try {
358
- // Run 'npm info <package> --json' to get metadata
359
- const { stdout } = await execa('npm', ['info', dep, '--json']);
360
- const info = JSON.parse(stdout);
361
-
362
- // 1. Check if package is deprecated
363
- if (info.deprecated) {
364
- issues.push({
365
- line: 0, // Package-level
366
- pattern: dep,
367
- severity: 'error',
368
- reason: `Package "${dep}" is marked as DEPRECATED in NPM registry: ${info.deprecated}`,
369
- replacement: 'Check package README for suggested alternatives',
370
- docs: `https://www.npmjs.com/package/${dep}`
371
- });
372
- }
373
-
374
- // 2. Check for latest version staleness
375
- const current = context.dependencies[dep].replace(/^[\^~>=<]+/, '');
376
- const latest = info['dist-tags']?.latest;
377
-
378
- if (latest && semver.major(latest) > semver.major(current)) {
379
- issues.push({
380
- line: 0,
381
- pattern: dep,
382
- severity: 'info',
383
- reason: `Package "${dep}" has a new major version available (${latest}). Your version: ${current}`,
384
- replacement: `npm install ${dep}@latest`,
385
- docs: `https://www.npmjs.com/package/${dep}`
386
- });
387
- }
388
- } catch (error) {
389
- // Silently skip if npm check fails
390
- continue;
391
- }
392
- }
393
-
394
- return issues;
395
- }
396
-
397
- /**
398
- * Load project context from package.json.
399
- */
400
- async loadProjectContext(): Promise<ProjectContext> {
401
- if (this.projectContext) {
402
- return this.projectContext;
403
- }
404
-
405
- const pkgPath = path.join(this.rootDir, 'package.json');
406
-
407
- try {
408
- const content = await fs.readFile(pkgPath, 'utf-8');
409
- const pkg = JSON.parse(content);
410
-
411
- this.projectContext = {
412
- dependencies: pkg.dependencies || {},
413
- devDependencies: pkg.devDependencies || {},
414
- nodeVersion: pkg.engines?.node,
415
- typescriptVersion: (pkg.devDependencies?.typescript || pkg.dependencies?.typescript)
416
- };
417
-
418
- return this.projectContext;
419
- } catch {
420
- this.projectContext = {
421
- dependencies: {},
422
- devDependencies: {}
423
- };
424
- return this.projectContext;
425
- }
426
- }
427
-
428
- /**
429
- * Check code for staleness issues.
430
- */
431
- async checkStaleness(code: string, filePath?: string, options: { live?: boolean } = {}): Promise<StalenessResult> {
432
- const context = await this.loadProjectContext();
433
- const issues: StalenessIssue[] = [];
434
-
435
- // 1. Check built-in/remote rules
436
- const lines = code.split('\n');
437
- for (const deprecation of this.deprecations) {
438
- // Check if this deprecation applies to the project
439
- if (deprecation.library && !this.hasLibrary(deprecation.library, context)) {
440
- continue;
441
- }
442
-
443
- // Check version constraints
444
- if (deprecation.library && deprecation.deprecatedIn !== 'ecosystem' &&
445
- deprecation.deprecatedIn !== 'best-practice' &&
446
- deprecation.deprecatedIn !== 'es6') {
447
-
448
- const installed = this.getInstalledVersion(deprecation.library, context);
449
- if (installed && !semver.gte(
450
- semver.coerce(installed) || '0.0.0',
451
- semver.coerce(deprecation.deprecatedIn) || '0.0.0'
452
- )) {
453
- // Project is on older version where this isn't deprecated yet
454
- continue;
455
- }
456
- }
457
-
458
- // Check for pattern match
459
- const regex = new RegExp(deprecation.pattern, 'g');
460
- for (let i = 0; i < lines.length; i++) {
461
- if (regex.test(lines[i])) {
462
- issues.push({
463
- line: i + 1,
464
- pattern: deprecation.pattern,
465
- severity: deprecation.severity,
466
- reason: deprecation.reason || `Deprecated in ${deprecation.deprecatedIn}`,
467
- replacement: deprecation.replacement,
468
- docs: deprecation.docs
469
- });
470
- }
471
- regex.lastIndex = 0;
472
- }
473
- }
474
-
475
- // 2. Perform live registry check if requested (only once per run/file usually)
476
- if (options.live) {
477
- const liveIssues = await this.checkLiveRegistry(context);
478
- issues.push(...liveIssues);
479
- }
480
-
481
- // Determine overall status
482
- let status: StalenessResult['status'] = 'FRESH';
483
- if (issues.some(i => i.severity === 'error')) {
484
- status = 'DEPRECATED';
485
- } else if (issues.some(i => i.severity === 'warning')) {
486
- status = 'STALE';
487
- }
488
-
489
- // Build project context for response
490
- const projectContextOutput: Record<string, string> = {};
491
- for (const [name, version] of Object.entries(context.dependencies)) {
492
- if (['react', 'react-dom', 'next', 'typescript', 'redux', 'express'].includes(name)) {
493
- projectContextOutput[name] = version;
494
- }
495
- }
496
- for (const [name, version] of Object.entries(context.devDependencies)) {
497
- if (['typescript'].includes(name)) {
498
- projectContextOutput[name] = version;
499
- }
500
- }
501
-
502
- return {
503
- status,
504
- issues,
505
- projectContext: projectContextOutput
506
- };
507
- }
508
-
509
- /**
510
- * Check if project has a library.
511
- */
512
- private hasLibrary(library: string, context: ProjectContext): boolean {
513
- return library in context.dependencies || library in context.devDependencies;
514
- }
515
-
516
- /**
517
- * Get installed version of a library.
518
- */
519
- private getInstalledVersion(library: string, context: ProjectContext): string | null {
520
- const version = context.dependencies[library] || context.devDependencies[library];
521
- if (!version) return null;
522
-
523
- // Remove version prefix (^, ~, >=, etc.)
524
- return version.replace(/^[\^~>=<]+/, '');
525
- }
526
-
527
- /**
528
- * Add custom deprecation rules.
529
- */
530
- addDeprecation(entry: DeprecationEntry): void {
531
- this.deprecations.push(entry);
532
- }
533
-
534
- /**
535
- * Load deprecations from a YAML file.
536
- */
537
- async loadDeprecationsFromFile(filePath: string): Promise<void> {
538
- try {
539
- const content = await fs.readFile(filePath, 'utf-8');
540
- const { parse } = await import('yaml');
541
- const data = parse(content);
542
-
543
- if (data.deprecations && Array.isArray(data.deprecations)) {
544
- this.deprecations.push(...data.deprecations);
545
- }
546
- } catch (error) {
547
- console.error(`Failed to load deprecations from ${filePath}:`, error);
548
- }
549
- }
550
-
551
- /**
552
- * Get all deprecations for display.
553
- */
554
- getAllDeprecations(): DeprecationEntry[] {
555
- return [...this.deprecations];
556
- }
557
- }
558
-
559
- /**
560
- * Quick helper to check code for staleness.
561
- */
562
- export async function checkCodeStaleness(
563
- rootDir: string,
564
- code: string
565
- ): Promise<StalenessResult> {
566
- const detector = new StalenessDetector(rootDir);
567
- return detector.checkStaleness(code);
568
- }