@manojkmfsi/monodog 1.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 (61) hide show
  1. package/.eslintrc.cjs +15 -0
  2. package/.turbo/turbo-build.log +4 -0
  3. package/CHANGELOG.md +79 -0
  4. package/LICENCE +21 -0
  5. package/README.md +55 -0
  6. package/dist/config-loader.js +116 -0
  7. package/dist/get-db-url.js +10 -0
  8. package/dist/gitService.js +242 -0
  9. package/dist/index.js +1370 -0
  10. package/dist/serve.js +103 -0
  11. package/dist/setup.js +155 -0
  12. package/dist/utils/ci-status.js +446 -0
  13. package/dist/utils/helpers.js +237 -0
  14. package/dist/utils/monorepo-scanner.js +486 -0
  15. package/dist/utils/utilities.js +414 -0
  16. package/monodog-conf.example.json +16 -0
  17. package/monodog-conf.json +16 -0
  18. package/monodog-dashboard/README.md +58 -0
  19. package/monodog-dashboard/dist/assets/index-2d967652.js +71 -0
  20. package/monodog-dashboard/dist/assets/index-504dc418.css +1 -0
  21. package/monodog-dashboard/dist/index.html +15 -0
  22. package/package.json +50 -0
  23. package/prisma/migrations/20251017041048_init/migration.sql +19 -0
  24. package/prisma/migrations/20251017083007_add_package/migration.sql +21 -0
  25. package/prisma/migrations/20251021083705_alter_package/migration.sql +37 -0
  26. package/prisma/migrations/20251022085155_test/migration.sql +2 -0
  27. package/prisma/migrations/20251022160841_/migration.sql +35 -0
  28. package/prisma/migrations/20251023130158_rename_column_name/migration.sql +34 -0
  29. package/prisma/migrations/20251023174837_/migration.sql +34 -0
  30. package/prisma/migrations/20251023175830_uodate_schema/migration.sql +32 -0
  31. package/prisma/migrations/20251024103700_add_dependency_info/migration.sql +13 -0
  32. package/prisma/migrations/20251025192150_add_dependency_info/migration.sql +19 -0
  33. package/prisma/migrations/20251025192342_add_dependency_info/migration.sql +40 -0
  34. package/prisma/migrations/20251025204613_add_dependency_info/migration.sql +8 -0
  35. package/prisma/migrations/20251026071336_add_dependency_info/migration.sql +25 -0
  36. package/prisma/migrations/20251027062626_add_commit/migration.sql +10 -0
  37. package/prisma/migrations/20251027062748_add_commit/migration.sql +23 -0
  38. package/prisma/migrations/20251027092741_add_commit/migration.sql +17 -0
  39. package/prisma/migrations/20251027112736_add_health_status/migration.sql +16 -0
  40. package/prisma/migrations/20251027140546_init_packages/migration.sql +16 -0
  41. package/prisma/migrations/20251029073436_added_package_heath_key/migration.sql +34 -0
  42. package/prisma/migrations/20251029073830_added_package_health_key/migration.sql +49 -0
  43. package/prisma/migrations/20251111091920_/migration.sql +16 -0
  44. package/prisma/migrations/20251211155036_cascade_on_auto_delete/migration.sql +48 -0
  45. package/prisma/migrations/migration_lock.toml +3 -0
  46. package/prisma/schema.prisma +114 -0
  47. package/release.config.js +41 -0
  48. package/src/config-loader.ts +119 -0
  49. package/src/get-db-url.ts +11 -0
  50. package/src/gitService.ts +277 -0
  51. package/src/index.ts +1554 -0
  52. package/src/serve.ts +87 -0
  53. package/src/setup.ts +164 -0
  54. package/src/types/monorepo-scanner.d.ts +32 -0
  55. package/src/utils/ci-status.ts +639 -0
  56. package/src/utils/helpers.js +203 -0
  57. package/src/utils/helpers.js.map +1 -0
  58. package/src/utils/helpers.ts +238 -0
  59. package/src/utils/monorepo-scanner.ts +599 -0
  60. package/src/utils/utilities.ts +483 -0
  61. package/tsconfig.json +16 -0
@@ -0,0 +1,486 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.scanner = exports.MonorepoScanner = void 0;
7
+ exports.quickScan = quickScan;
8
+ exports.generateReports = generateReports;
9
+ exports.scanForFiles = scanForFiles;
10
+ exports.funCheckBuildStatus = funCheckBuildStatus;
11
+ exports.funCheckTestCoverage = funCheckTestCoverage;
12
+ exports.funCheckLintStatus = funCheckLintStatus;
13
+ exports.funCheckSecurityAudit = funCheckSecurityAudit;
14
+ // Monorepo Scanner
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const path_1 = __importDefault(require("path"));
17
+ const child_process_1 = require("child_process");
18
+ const utilities_1 = require("./utilities");
19
+ class MonorepoScanner {
20
+ constructor(rootDir = process.cwd()) {
21
+ this.cache = new Map();
22
+ this.cacheExpiry = 5 * 60 * 1000; // 5 minutes
23
+ this.rootDir = rootDir;
24
+ }
25
+ /**
26
+ * Performs a complete scan of the monorepo
27
+ */
28
+ async scan() {
29
+ const startTime = Date.now();
30
+ try {
31
+ // Check cache first
32
+ const cacheKey = 'full-scan';
33
+ const cached = this.getFromCache(cacheKey);
34
+ if (cached) {
35
+ return cached;
36
+ }
37
+ console.log('🔍 Starting monorepo scan...');
38
+ // Scan all packages
39
+ const packages = (0, utilities_1.scanMonorepo)(this.rootDir);
40
+ console.log(`📦 Found ${packages.length} packages`);
41
+ // Generate statistics
42
+ const stats = (0, utilities_1.generateMonorepoStats)(packages);
43
+ // Generate dependency graph
44
+ const dependencyGraph = (0, utilities_1.generateDependencyGraph)(packages);
45
+ // Find circular dependencies
46
+ const circularDependencies = (0, utilities_1.findCircularDependencies)(packages);
47
+ // Check for outdated packages
48
+ const outdatedPackages = this.findOutdatedPackages(packages);
49
+ const result = {
50
+ packages,
51
+ stats,
52
+ dependencyGraph,
53
+ circularDependencies,
54
+ outdatedPackages,
55
+ scanTimestamp: new Date(),
56
+ scanDuration: Date.now() - startTime,
57
+ };
58
+ // Cache the result
59
+ this.setCache(cacheKey, result);
60
+ console.log(`✅ Scan completed in ${result.scanDuration}ms`);
61
+ return result;
62
+ }
63
+ catch (error) {
64
+ console.error('❌ Error during scan:', error);
65
+ throw error;
66
+ }
67
+ }
68
+ /**
69
+ * Generates detailed reports for all packages
70
+ */
71
+ async generatePackageReports() {
72
+ const packages = (0, utilities_1.scanMonorepo)(this.rootDir);
73
+ const reports = [];
74
+ for (const pkg of packages) {
75
+ try {
76
+ const report = await this.generatePackageReport(pkg);
77
+ reports.push(report);
78
+ }
79
+ catch (error) {
80
+ console.error(`Error generating report for ${pkg.name}:`, error);
81
+ }
82
+ }
83
+ return reports;
84
+ }
85
+ /**
86
+ * Generates a detailed report for a specific package
87
+ */
88
+ async generatePackageReport(pkg) {
89
+ const health = await this.assessPackageHealth(pkg);
90
+ const size = (0, utilities_1.getPackageSize)(pkg.path);
91
+ const outdatedDeps = (0, utilities_1.checkOutdatedDependencies)(pkg);
92
+ const lastModified = this.getLastModified(pkg.path);
93
+ const gitInfo = await this.getGitInfo(pkg.path);
94
+ return {
95
+ package: pkg,
96
+ health,
97
+ size,
98
+ outdatedDeps,
99
+ lastModified,
100
+ gitInfo,
101
+ };
102
+ }
103
+ /**
104
+ * Assesses the health of a package
105
+ */
106
+ async assessPackageHealth(pkg) {
107
+ // Check build status
108
+ const buildStatus = await this.checkBuildStatus(pkg);
109
+ // Check test coverage
110
+ const testCoverage = await this.checkTestCoverage(pkg);
111
+ // Check lint status
112
+ const lintStatus = await this.checkLintStatus(pkg);
113
+ // Check security audit
114
+ const securityAudit = await this.checkSecurityAudit(pkg);
115
+ return (0, utilities_1.calculatePackageHealth)(buildStatus, testCoverage, lintStatus, securityAudit);
116
+ }
117
+ /**
118
+ * Checks if a package builds successfully
119
+ */
120
+ async checkBuildStatus(pkg) {
121
+ try {
122
+ if (pkg.scripts.build) {
123
+ // Try to run build command
124
+ (0, child_process_1.execSync)('npm run build', {
125
+ cwd: pkg.path,
126
+ stdio: 'pipe',
127
+ timeout: 30000,
128
+ });
129
+ return 'success';
130
+ }
131
+ return 'unknown';
132
+ }
133
+ catch (error) {
134
+ return 'failed';
135
+ }
136
+ }
137
+ /**
138
+ * Checks test coverage for a package
139
+ */
140
+ // async checkTestCoverage(pkg: PackageInfo): Promise<number> {
141
+ // try {
142
+ // if (pkg.scripts.test) {
143
+ // // This would typically run tests and parse coverage reports
144
+ // // For now, return a mock value
145
+ // return Math.floor(Math.random() * 100);
146
+ // }
147
+ // return 0;
148
+ // } catch (error) {
149
+ // return 0;
150
+ // }
151
+ // }
152
+ async checkTestCoverage(pkg) {
153
+ try {
154
+ // First, check for existing coverage reports
155
+ const coveragePaths = [
156
+ path_1.default.join(pkg.path, 'coverage', 'coverage-summary.json'),
157
+ path_1.default.join(pkg.path, 'coverage', 'lcov.info'),
158
+ path_1.default.join(pkg.path, 'coverage', 'clover.xml'),
159
+ path_1.default.join(pkg.path, 'coverage.json'),
160
+ ];
161
+ // Look for any coverage file that exists
162
+ for (const coveragePath of coveragePaths) {
163
+ if (fs_1.default.existsSync(coveragePath)) {
164
+ if (coveragePath.endsWith('coverage-summary.json')) {
165
+ try {
166
+ const coverage = JSON.parse(fs_1.default.readFileSync(coveragePath, 'utf8'));
167
+ return (coverage.total?.lines?.pct ||
168
+ coverage.total?.statements?.pct ||
169
+ 0);
170
+ }
171
+ catch (error) {
172
+ console.warn(`Error parsing coverage file for ${pkg.name}:`, error);
173
+ }
174
+ }
175
+ // If we find any coverage file but can't parse it, assume coverage exists
176
+ return 50; // Default coverage if files exist but can't parse
177
+ }
178
+ }
179
+ // If no coverage files found and package has test script
180
+ if (pkg.scripts.test) {
181
+ // Return a reasonable default based on whether tests are likely to have coverage
182
+ const hasCoverageSetup = pkg.scripts.test.includes('--coverage') ||
183
+ pkg.scripts.test.includes('coverage') ||
184
+ pkg.devDependencies?.jest ||
185
+ pkg.devDependencies?.nyc ||
186
+ pkg.devDependencies?.['@types/jest'];
187
+ return hasCoverageSetup ? 30 : 0; // Reasonable defaults
188
+ }
189
+ return 0;
190
+ }
191
+ catch (error) {
192
+ console.warn(`Error checking coverage for ${pkg.name}:`, error);
193
+ return 0;
194
+ }
195
+ }
196
+ /**
197
+ * Checks lint status for a package
198
+ */
199
+ async checkLintStatus(pkg) {
200
+ try {
201
+ if (pkg.scripts.lint) {
202
+ // Try to run lint command
203
+ (0, child_process_1.execSync)('npm run lint', {
204
+ cwd: pkg.path,
205
+ stdio: 'pipe',
206
+ timeout: 10000,
207
+ });
208
+ return 'pass';
209
+ }
210
+ return 'unknown';
211
+ }
212
+ catch (error) {
213
+ return 'fail';
214
+ }
215
+ }
216
+ /**
217
+ * Checks security audit for a package
218
+ */
219
+ async checkSecurityAudit(pkg) {
220
+ try {
221
+ // Run npm audit
222
+ const result = (0, child_process_1.execSync)('npm audit --json', {
223
+ cwd: pkg.path,
224
+ stdio: 'pipe',
225
+ timeout: 15000,
226
+ });
227
+ const audit = JSON.parse(result.toString());
228
+ return audit.metadata.vulnerabilities.total === 0 ? 'pass' : 'fail';
229
+ }
230
+ catch (error) {
231
+ return 'unknown';
232
+ }
233
+ }
234
+ /**
235
+ * Finds packages with outdated dependencies
236
+ */
237
+ findOutdatedPackages(packages) {
238
+ const outdated = [];
239
+ for (const pkg of packages) {
240
+ const outdatedDeps = (0, utilities_1.checkOutdatedDependencies)(pkg);
241
+ if (outdatedDeps.length > 0) {
242
+ outdated.push(pkg.name);
243
+ }
244
+ }
245
+ return outdated;
246
+ }
247
+ /**
248
+ * Gets the last modified date of a package
249
+ */
250
+ getLastModified(packagePath) {
251
+ try {
252
+ const stats = fs_1.default.statSync(packagePath);
253
+ return stats.mtime;
254
+ }
255
+ catch (error) {
256
+ return new Date();
257
+ }
258
+ }
259
+ /**
260
+ * Gets git information for a package
261
+ */
262
+ async getGitInfo(packagePath) {
263
+ try {
264
+ // Check if this is a git repository
265
+ const gitPath = path_1.default.join(packagePath, '.git');
266
+ if (!fs_1.default.existsSync(gitPath)) {
267
+ return undefined;
268
+ }
269
+ // Get last commit info
270
+ const lastCommit = (0, child_process_1.execSync)('git rev-parse HEAD', {
271
+ cwd: packagePath,
272
+ stdio: 'pipe',
273
+ })
274
+ .toString()
275
+ .trim();
276
+ const lastCommitDate = new Date((0, child_process_1.execSync)('git log -1 --format=%cd', {
277
+ cwd: packagePath,
278
+ stdio: 'pipe',
279
+ })
280
+ .toString()
281
+ .trim());
282
+ const author = (0, child_process_1.execSync)('git log -1 --format=%an', {
283
+ cwd: packagePath,
284
+ stdio: 'pipe',
285
+ })
286
+ .toString()
287
+ .trim();
288
+ const branch = (0, child_process_1.execSync)('git branch --show-current', {
289
+ cwd: packagePath,
290
+ stdio: 'pipe',
291
+ })
292
+ .toString()
293
+ .trim();
294
+ return {
295
+ lastCommit: lastCommit.substring(0, 7),
296
+ lastCommitDate,
297
+ author,
298
+ branch,
299
+ };
300
+ }
301
+ catch (error) {
302
+ return undefined;
303
+ }
304
+ }
305
+ /**
306
+ * Scans for specific file types in packages
307
+ */
308
+ scanForFileTypes(fileTypes) {
309
+ const results = {};
310
+ for (const fileType of fileTypes) {
311
+ results[fileType] = [];
312
+ }
313
+ const packages = (0, utilities_1.scanMonorepo)(this.rootDir);
314
+ for (const pkg of packages) {
315
+ this.scanPackageForFiles(pkg.path, fileTypes, results);
316
+ }
317
+ return results;
318
+ }
319
+ /**
320
+ * Recursively scans a package directory for specific file types
321
+ */
322
+ scanPackageForFiles(packagePath, fileTypes, results) {
323
+ try {
324
+ const items = fs_1.default.readdirSync(packagePath, { withFileTypes: true });
325
+ for (const item of items) {
326
+ const fullPath = path_1.default.join(packagePath, item.name);
327
+ if (item.isDirectory()) {
328
+ // Skip certain directories
329
+ if (!['node_modules', 'dist', 'build', '.git'].includes(item.name)) {
330
+ this.scanPackageForFiles(fullPath, fileTypes, results);
331
+ }
332
+ }
333
+ else {
334
+ // Check file extension
335
+ const ext = path_1.default.extname(item.name);
336
+ if (fileTypes.includes(ext)) {
337
+ results[ext].push(fullPath);
338
+ }
339
+ }
340
+ }
341
+ }
342
+ catch (error) {
343
+ // Skip directories we can't read
344
+ }
345
+ }
346
+ /**
347
+ * Gets cache value if not expired
348
+ */
349
+ getFromCache(key) {
350
+ const cached = this.cache.get(key);
351
+ if (cached && Date.now() - cached.timestamp < this.cacheExpiry) {
352
+ return cached.data;
353
+ }
354
+ return null;
355
+ }
356
+ /**
357
+ * Sets cache value with timestamp
358
+ */
359
+ setCache(key, data) {
360
+ this.cache.set(key, {
361
+ data,
362
+ timestamp: Date.now(),
363
+ });
364
+ }
365
+ /**
366
+ * Clears the cache
367
+ */
368
+ clearCache() {
369
+ this.cache.clear();
370
+ }
371
+ /**
372
+ * Exports scan results to various formats
373
+ */
374
+ exportResults(results, format) {
375
+ switch (format) {
376
+ case 'json':
377
+ return JSON.stringify(results, null, 2);
378
+ case 'csv':
379
+ return this.exportToCSV(results);
380
+ case 'html':
381
+ return this.exportToHTML(results);
382
+ default:
383
+ throw new Error(`Unsupported format: ${format}`);
384
+ }
385
+ }
386
+ /**
387
+ * Exports results to CSV format
388
+ */
389
+ exportToCSV(results) {
390
+ const headers = [
391
+ 'Package',
392
+ 'Type',
393
+ 'Version',
394
+ 'Dependencies',
395
+ 'Health Score',
396
+ ];
397
+ const rows = results.packages.map(pkg => [
398
+ pkg.name,
399
+ pkg.type,
400
+ pkg.version,
401
+ Object.keys(pkg.dependencies).length,
402
+ 'N/A', // Would need health calculation
403
+ ]);
404
+ return [headers, ...rows]
405
+ .map(row => row.map(cell => `"${cell}"`).join(','))
406
+ .join('\n');
407
+ }
408
+ /**
409
+ * Exports results to HTML format
410
+ */
411
+ exportToHTML(results) {
412
+ return `
413
+ <!DOCTYPE html>
414
+ <html>
415
+ <head>
416
+ <title>Monorepo Scan Report</title>
417
+ <style>
418
+ body { font-family: Arial, sans-serif; margin: 20px; }
419
+ table { border-collapse: collapse; width: 100%; }
420
+ th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
421
+ th { background-color: #f2f2f2; }
422
+ </style>
423
+ </head>
424
+ <body>
425
+ <h1>Monorepo Scan Report</h1>
426
+ <p>Generated: ${results.scanTimestamp}</p>
427
+ <p>Duration: ${results.scanDuration}ms</p>
428
+
429
+ <h2>Summary</h2>
430
+ <ul>
431
+ <li>Total Packages: ${results.stats.totalPackages}</li>
432
+ <li>Applications: ${results.stats.apps}</li>
433
+ <li>Libraries: ${results.stats.libraries}</li>
434
+ <li>Tools: ${results.stats.tools}</li>
435
+ </ul>
436
+
437
+ <h2>Packages</h2>
438
+ <table>
439
+ <tr>
440
+ <th>Name</th>
441
+ <th>Type</th>
442
+ <th>Version</th>
443
+ <th>Dependencies</th>
444
+ </tr>
445
+ ${results.packages
446
+ .map(pkg => `
447
+ <tr>
448
+ <td>${pkg.name}</td>
449
+ <td>${pkg.type}</td>
450
+ <td>${pkg.version}</td>
451
+ <td>${Object.keys(pkg.dependencies).length}</td>
452
+ </tr>
453
+ `)
454
+ .join('')}
455
+ </table>
456
+ </body>
457
+ </html>
458
+ `;
459
+ }
460
+ }
461
+ exports.MonorepoScanner = MonorepoScanner;
462
+ // Export default instance
463
+ exports.scanner = new MonorepoScanner();
464
+ // Export convenience functions
465
+ async function quickScan() {
466
+ return exports.scanner.scan();
467
+ }
468
+ async function generateReports() {
469
+ return exports.scanner.generatePackageReports();
470
+ }
471
+ function scanForFiles(fileTypes) {
472
+ return exports.scanner.scanForFileTypes(fileTypes);
473
+ }
474
+ // Fix these function signatures - they should accept single PackageInfo objects
475
+ async function funCheckBuildStatus(pkg) {
476
+ return exports.scanner.checkBuildStatus(pkg);
477
+ }
478
+ async function funCheckTestCoverage(pkg) {
479
+ return exports.scanner.checkTestCoverage(pkg);
480
+ }
481
+ async function funCheckLintStatus(pkg) {
482
+ return exports.scanner.checkLintStatus(pkg);
483
+ }
484
+ async function funCheckSecurityAudit(pkg) {
485
+ return exports.scanner.checkSecurityAudit(pkg);
486
+ }