@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,414 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.resolveWorkspaceGlobs = resolveWorkspaceGlobs;
40
+ exports.getWorkspacesFromRoot = getWorkspacesFromRoot;
41
+ exports.parsePackageInfo = parsePackageInfo;
42
+ exports.scanMonorepo = scanMonorepo;
43
+ exports.generateMonorepoStats = generateMonorepoStats;
44
+ exports.findCircularDependencies = findCircularDependencies;
45
+ exports.generateDependencyGraph = generateDependencyGraph;
46
+ exports.checkOutdatedDependencies = checkOutdatedDependencies;
47
+ exports.getPackageSize = getPackageSize;
48
+ exports.calculatePackageHealth = calculatePackageHealth;
49
+ // import { Package } from '@prisma/client';
50
+ const fs = __importStar(require("fs"));
51
+ const path_1 = __importDefault(require("path"));
52
+ const config_loader_1 = require("../config-loader");
53
+ /**
54
+ * Resolves simple workspace globs (like 'packages/*', 'apps/*') into actual package directory paths.
55
+ * Note: This implementation only handles the 'folder/*' pattern and is not a full glob resolver.
56
+ */
57
+ function resolveWorkspaceGlobs(rootDir, globs) {
58
+ const resolvedPaths = [];
59
+ for (const glob of globs) {
60
+ if (glob.endsWith('/*')) {
61
+ const baseDirName = glob.slice(0, -2); // e.g., 'packages'
62
+ const baseDirPath = path_1.default.join(rootDir, baseDirName);
63
+ if (fs.existsSync(baseDirPath) && fs.statSync(baseDirPath).isDirectory()) {
64
+ const subDirs = fs.readdirSync(baseDirPath, { withFileTypes: true })
65
+ .filter(dirent => dirent.isDirectory())
66
+ .map(dirent => path_1.default.join(baseDirName, dirent.name)); // e.g., 'packages/my-utils'
67
+ resolvedPaths.push(...subDirs);
68
+ }
69
+ }
70
+ else {
71
+ // Handle non-glob paths (e.g., 'packages/my-package') if it's explicitly listed
72
+ const directPath = path_1.default.join(rootDir, glob);
73
+ if (fs.existsSync(directPath) && fs.statSync(directPath).isDirectory()) {
74
+ resolvedPaths.push(glob);
75
+ }
76
+ }
77
+ }
78
+ return resolvedPaths;
79
+ }
80
+ /**
81
+ * Reads the root package.json and extracts the 'workspaces' field (array of globs).
82
+ */
83
+ function getWorkspacesFromRoot(rootDir) {
84
+ const packageJsonPath = path_1.default.join(rootDir, 'package.json');
85
+ if (!fs.existsSync(packageJsonPath)) {
86
+ console.warn(`\n⚠️ Warning: No package.json found at root directory: ${rootDir}`);
87
+ return undefined;
88
+ }
89
+ try {
90
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
91
+ // Handle both standard array and object format (used by yarn/pnpm)
92
+ if (Array.isArray(packageJson.workspaces)) {
93
+ return packageJson.workspaces;
94
+ }
95
+ else if (packageJson.workspaces && Array.isArray(packageJson.workspaces.packages)) {
96
+ return packageJson.workspaces.packages;
97
+ }
98
+ }
99
+ catch (e) {
100
+ console.error(`\n❌ Error parsing package.json at ${packageJsonPath}. Skipping workspace detection.`);
101
+ }
102
+ return undefined;
103
+ }
104
+ /**
105
+ * Scans the monorepo and returns information about all packages
106
+ */
107
+ function scanMonorepo(rootDir) {
108
+ const packages = [];
109
+ console.log('rootDir:', rootDir);
110
+ const workspacesGlobs = config_loader_1.appConfig.workspaces;
111
+ // Use provided workspaces globs if given, otherwise attempt to detect from root package.json
112
+ const detectedWorkspacesGlobs = workspacesGlobs.length > 0 ? workspacesGlobs : getWorkspacesFromRoot(rootDir);
113
+ if (detectedWorkspacesGlobs && detectedWorkspacesGlobs.length > 0) {
114
+ if (workspacesGlobs.length) {
115
+ console.log(`\n✅ Using provided workspaces globs: ${detectedWorkspacesGlobs.join(', ')}`);
116
+ }
117
+ else {
118
+ console.log(`\n✅ Detected Monorepo Workspaces Globs: ${detectedWorkspacesGlobs.join(', ')}`);
119
+ }
120
+ // 1. Resolve the globs into concrete package directory paths
121
+ const resolvedPackagePaths = resolveWorkspaceGlobs(rootDir, detectedWorkspacesGlobs);
122
+ console.log(`[DEBUG] Resolved package directories (Total ${resolvedPackagePaths.length}):`);
123
+ console.warn(resolvedPackagePaths.length < workspacesGlobs.length ? 'Some workspaces globs provided are invalid.' : '');
124
+ // 2. Integration of the requested loop structure for package scanning
125
+ for (const workspacePath of resolvedPackagePaths) {
126
+ const fullPackagePath = path_1.default.join(rootDir, workspacePath);
127
+ // The package name would be read from the package.json inside this path
128
+ const packageName = path_1.default.basename(fullPackagePath);
129
+ console.log(`- Scanning path: ${workspacePath} (Package: ${packageName})`);
130
+ const packageInfo = parsePackageInfo(fullPackagePath, packageName);
131
+ if (packageInfo) {
132
+ packages.push(packageInfo);
133
+ }
134
+ }
135
+ }
136
+ else {
137
+ console.warn('\n⚠️ No workspace globs provided or detected. Returning empty package list.');
138
+ }
139
+ return packages;
140
+ }
141
+ /*** Parses package.json and determines package type */
142
+ function parsePackageInfo(packagePath, packageName, forcedType) {
143
+ const packageJsonPath = path_1.default.join(packagePath, 'package.json');
144
+ if (!fs.existsSync(packageJsonPath)) {
145
+ return null;
146
+ }
147
+ try {
148
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
149
+ // Determine package type
150
+ let packageType = 'lib';
151
+ if (forcedType) {
152
+ packageType = forcedType;
153
+ }
154
+ else if (packageJson.scripts && packageJson.scripts.start) {
155
+ packageType = 'app';
156
+ }
157
+ else if (packageJson.keywords && packageJson.keywords.includes('tool')) {
158
+ packageType = 'tool';
159
+ }
160
+ return {
161
+ name: packageJson.name || packageName,
162
+ version: packageJson.version || '0.0.0',
163
+ type: packageType,
164
+ path: packagePath,
165
+ dependencies: packageJson.dependencies || {},
166
+ devDependencies: packageJson.devDependencies || {},
167
+ peerDependencies: packageJson.peerDependencies || {},
168
+ scripts: packageJson.scripts || {},
169
+ maintainers: packageJson.maintainers || [],
170
+ description: packageJson.description,
171
+ license: packageJson.license,
172
+ repository: packageJson.repository || {},
173
+ };
174
+ }
175
+ catch (error) {
176
+ console.error(`Error parsing package.json for ${packageName}:`, error);
177
+ return null;
178
+ }
179
+ }
180
+ /**
181
+ * Analyzes dependencies and determines their status
182
+ */
183
+ // function analyzeDependencies(
184
+ // dependencies: Record<string, string>,
185
+ // type: 'production' | 'development' = 'production'
186
+ // ): DependencyInfo[] {
187
+ // return Object.entries(dependencies).map(([name, version]) => ({
188
+ // name,
189
+ // currentVersion: version,
190
+ // status: 'unknown', // Would be determined by npm registry check
191
+ // type,
192
+ // }));
193
+ // }
194
+ /**
195
+ * Calculates package health score based on various metrics
196
+ */
197
+ function calculatePackageHealth(buildStatus, testCoverage, lintStatus, securityAudit) {
198
+ let score = 0;
199
+ // Build status (30 points)
200
+ switch (buildStatus) {
201
+ case 'success':
202
+ score += 30;
203
+ break;
204
+ case 'running':
205
+ score += 15;
206
+ break;
207
+ case 'failed':
208
+ score += 0;
209
+ break;
210
+ default:
211
+ score += 10;
212
+ }
213
+ // Test coverage (25 points)
214
+ score += Math.min(25, (testCoverage / 100) * 25);
215
+ // Lint status (25 points)
216
+ switch (lintStatus) {
217
+ case 'pass':
218
+ score += 25;
219
+ break;
220
+ case 'fail':
221
+ score += 0;
222
+ break;
223
+ default:
224
+ score += 10;
225
+ }
226
+ // Security audit (20 points)
227
+ switch (securityAudit) {
228
+ case 'pass':
229
+ score += 20;
230
+ break;
231
+ case 'fail':
232
+ score += 0;
233
+ break;
234
+ default:
235
+ score += 10;
236
+ }
237
+ return {
238
+ buildStatus,
239
+ testCoverage,
240
+ lintStatus,
241
+ securityAudit,
242
+ overallScore: Math.round(score),
243
+ };
244
+ }
245
+ /**
246
+ * Generates comprehensive monorepo statistics
247
+ */
248
+ function generateMonorepoStats(packages) {
249
+ const stats = {
250
+ totalPackages: packages.length,
251
+ apps: packages.filter(p => p.type === 'app').length,
252
+ libraries: packages.filter(p => p.type === 'lib').length,
253
+ tools: packages.filter(p => p.type === 'tool').length,
254
+ healthyPackages: 0,
255
+ warningPackages: 0,
256
+ errorPackages: 0,
257
+ outdatedDependencies: 0,
258
+ totalDependencies: 0,
259
+ };
260
+ // Calculate dependency counts
261
+ packages.forEach(pkg => {
262
+ stats.totalDependencies += Object.keys(pkg.dependencies).length;
263
+ stats.totalDependencies += Object.keys(pkg.devDependencies).length;
264
+ stats.totalDependencies += Object.keys(pkg.peerDependencies ?? {}).length;
265
+ });
266
+ return stats;
267
+ }
268
+ /**
269
+ * Finds circular dependencies in the monorepo
270
+ */
271
+ function findCircularDependencies(packages) {
272
+ const graph = new Map();
273
+ const visited = new Set();
274
+ const recursionStack = new Set();
275
+ const circularDeps = [];
276
+ // Build dependency graph
277
+ packages.forEach(pkg => {
278
+ graph.set(pkg.name, Object.keys(pkg.dependencies));
279
+ });
280
+ function dfs(node, path) {
281
+ if (recursionStack.has(node)) {
282
+ const cycleStart = path.indexOf(node);
283
+ circularDeps.push(path.slice(cycleStart));
284
+ return;
285
+ }
286
+ if (visited.has(node)) {
287
+ return;
288
+ }
289
+ visited.add(node);
290
+ recursionStack.add(node);
291
+ path.push(node);
292
+ const dependencies = graph.get(node) || [];
293
+ for (const dep of dependencies) {
294
+ if (graph.has(dep)) {
295
+ dfs(dep, [...path]);
296
+ }
297
+ }
298
+ recursionStack.delete(node);
299
+ }
300
+ for (const node of graph.keys()) {
301
+ if (!visited.has(node)) {
302
+ dfs(node, []);
303
+ }
304
+ }
305
+ return circularDeps;
306
+ }
307
+ /**
308
+ * Generates a dependency graph for visualization
309
+ */
310
+ function generateDependencyGraph(packages) {
311
+ const nodes = packages.map(pkg => ({
312
+ // id: pkg.name,
313
+ label: pkg.name,
314
+ type: pkg.type,
315
+ version: pkg.version,
316
+ dependencies: Object.keys(pkg.dependencies).length,
317
+ }));
318
+ const edges = [];
319
+ packages.forEach(pkg => {
320
+ Object.keys(pkg.dependencies).forEach(depName => {
321
+ // Only include internal dependencies
322
+ if (packages.some(p => p.name === depName)) {
323
+ edges.push({
324
+ from: pkg.name,
325
+ to: depName,
326
+ type: 'internal',
327
+ });
328
+ }
329
+ });
330
+ });
331
+ return { nodes, edges };
332
+ }
333
+ /**
334
+ * Checks if a package has outdated dependencies
335
+ */
336
+ function checkOutdatedDependencies(packageInfo) {
337
+ const outdated = [];
338
+ // This would typically involve checking against npm registry
339
+ // For now, we'll simulate with some basic checks
340
+ Object.entries(packageInfo.dependencies).forEach(([name, version]) => {
341
+ if (version.startsWith('^') || version.startsWith('~')) {
342
+ // Could be outdated, would need registry check
343
+ outdated.push({
344
+ name,
345
+ version: version,
346
+ status: 'unknown',
347
+ type: 'dependency',
348
+ });
349
+ }
350
+ });
351
+ return outdated;
352
+ }
353
+ /**
354
+ * Formats version numbers for comparison
355
+ */
356
+ // function parseVersion(version: string): number[] {
357
+ // return version
358
+ // .replace(/^[^0-9]*/, '')
359
+ // .split('.')
360
+ // .map(Number);
361
+ // }
362
+ /**
363
+ * Compares two version strings
364
+ */
365
+ // function compareVersions(v1: string, v2: string): number {
366
+ // const parsed1 = parseVersion(v1);
367
+ // const parsed2 = parseVersion(v2);
368
+ // for (let i = 0; i < Math.max(parsed1.length, parsed2.length); i++) {
369
+ // const num1 = parsed1[i] || 0;
370
+ // const num2 = parsed2[i] || 0;
371
+ // if (num1 > num2) return 1;
372
+ // if (num1 < num2) return -1;
373
+ // }
374
+ // return 0;
375
+ // }
376
+ /**
377
+ * Gets package size information
378
+ */
379
+ function getPackageSize(packagePath) {
380
+ try {
381
+ let totalSize = 0;
382
+ let fileCount = 0;
383
+ const calculateSize = (dirPath) => {
384
+ const items = fs.readdirSync(dirPath, { withFileTypes: true });
385
+ for (const item of items) {
386
+ const fullPath = path_1.default.join(dirPath, item.name);
387
+ if (item.isDirectory()) {
388
+ // Skip node_modules and other build artifacts
389
+ if (!['node_modules', 'dist', 'build', '.git'].includes(item.name)) {
390
+ calculateSize(fullPath);
391
+ }
392
+ }
393
+ else {
394
+ try {
395
+ const stats = fs.statSync(fullPath);
396
+ totalSize += stats.size;
397
+ fileCount++;
398
+ }
399
+ catch (error) {
400
+ // Skip files we can't read
401
+ }
402
+ }
403
+ }
404
+ };
405
+ calculateSize(packagePath);
406
+ return {
407
+ size: totalSize,
408
+ files: fileCount,
409
+ };
410
+ }
411
+ catch (error) {
412
+ return { size: 0, files: 0 };
413
+ }
414
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "workspaces": [
3
+ "packages/*"
4
+ ],
5
+ "database": {
6
+ "path": "file:./monodog.db"
7
+ },
8
+ "dashboard": {
9
+ "host": "0.0.0.0",
10
+ "port": "3010"
11
+ },
12
+ "server": {
13
+ "host": "0.0.0.0",
14
+ "port": 8999
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "workspaces": [
3
+ "packages/*"
4
+ ],
5
+ "database": {
6
+ "path": "file:./monodog.db"
7
+ },
8
+ "dashboard": {
9
+ "host": "0.0.0.0",
10
+ "port": "3010"
11
+ },
12
+ "server": {
13
+ "host": "0.0.0.0",
14
+ "port": 8999
15
+ }
16
+ }
@@ -0,0 +1,58 @@
1
+ # Monodog: Monorepo Health Dashboard (Frontend)
2
+
3
+ ## 🎯 Overview
4
+
5
+ This is the **client-side application** designed to consume data from the **Monorepo Analytics and Health API** backend service.
6
+ It provides a **real-time, visual dashboard** for tracking the health, dependencies, and overall status of all packages within the monorepo.
7
+
8
+ ---
9
+
10
+ ## 🛠 Technology Stack
11
+
12
+ | Component | Technology | Description |
13
+ | ----------------- | ----------------------------- | --------------------------------------------------------------------- |
14
+ | **Framework** | React (Functional Components) | Core library for building the user interface. |
15
+ | **Styling** | Tailwind CSS | Utility-first framework for responsive, modern, and aesthetic design. |
16
+ | **Data Fetching** | Fetch API (Native JavaScript) | Handles communication with the backend Express API. |
17
+ | **Icons** | Lucide React | Simple, clean vector icons for visualization. |
18
+
19
+ ---
20
+
21
+ ## ⚙️ Prerequisites
22
+
23
+ To run this application, ensure the following:
24
+
25
+ - **Node.js** and a package manager (`npm`, `yarn`, or `pnpm`) are installed.
26
+ - The **Monorepo Analytics API** backend service is running and accessible.
27
+ - Default backend URL: **http://localhost:8999**
28
+
29
+ ---
30
+
31
+ ## 🚨 API Connection Details
32
+
33
+ The dashboard connects to the backend API using the following base URL:
34
+
35
+ ```javascript
36
+ const API_BASE_URL = 'http://localhost:8999/api';
37
+ ```
38
+
39
+ ## 🚀 Getting Started
40
+
41
+ ### Installation
42
+
43
+ Clone the repository and install the dependencies:
44
+
45
+ ```bash
46
+ # Clone the repository
47
+ git clone https://github.com/mindfiredigital/MonoDog.git
48
+ cd packages/monoapp/monodog-dashboard
49
+
50
+ # Install dependencies
51
+ npm install
52
+
53
+ # build
54
+ npm run build
55
+
56
+ # run dashboard
57
+ npm run dev
58
+ ```