@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,446 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ciStatusManager = exports.CIStatusManager = void 0;
4
+ exports.getPackageCIStatus = getPackageCIStatus;
5
+ exports.getMonorepoCIStatus = getMonorepoCIStatus;
6
+ exports.triggerPackageBuild = triggerPackageBuild;
7
+ class CIStatusManager {
8
+ constructor() {
9
+ this.providers = new Map();
10
+ this.cache = new Map();
11
+ this.cacheExpiry = 2 * 60 * 1000; // 2 minutes
12
+ this.initializeDefaultProviders();
13
+ }
14
+ /**
15
+ * Initialize default CI providers
16
+ */
17
+ initializeDefaultProviders() {
18
+ // GitHub Actions
19
+ this.addProvider({
20
+ name: 'GitHub Actions',
21
+ type: 'github',
22
+ baseUrl: 'https://api.github.com',
23
+ });
24
+ // GitLab CI
25
+ this.addProvider({
26
+ name: 'GitLab CI',
27
+ type: 'gitlab',
28
+ baseUrl: 'https://gitlab.com/api/v4',
29
+ });
30
+ // CircleCI
31
+ this.addProvider({
32
+ name: 'CircleCI',
33
+ type: 'circleci',
34
+ baseUrl: 'https://circleci.com/api/v2',
35
+ });
36
+ }
37
+ /**
38
+ * Add a CI provider
39
+ */
40
+ addProvider(provider) {
41
+ this.providers.set(provider.name, provider);
42
+ }
43
+ /**
44
+ * Remove a CI provider
45
+ */
46
+ removeProvider(name) {
47
+ this.providers.delete(name);
48
+ }
49
+ /**
50
+ * Get all registered providers
51
+ */
52
+ getProviders() {
53
+ return Array.from(this.providers.values());
54
+ }
55
+ /**
56
+ * Fetch CI status for a specific package
57
+ */
58
+ async getPackageStatus(packageName, providerName) {
59
+ const cacheKey = `package-status-${packageName}-${providerName || 'all'}`;
60
+ const cached = this.getFromCache(cacheKey);
61
+ if (cached) {
62
+ return cached;
63
+ }
64
+ try {
65
+ let builds = [];
66
+ if (providerName) {
67
+ const provider = this.providers.get(providerName);
68
+ if (provider) {
69
+ builds = await this.fetchBuildsFromProvider(provider, packageName);
70
+ }
71
+ }
72
+ else {
73
+ // Fetch from all providers
74
+ for (const provider of this.providers.values()) {
75
+ const providerBuilds = await this.fetchBuildsFromProvider(provider, packageName);
76
+ builds.push(...providerBuilds);
77
+ }
78
+ }
79
+ if (builds.length === 0) {
80
+ return null;
81
+ }
82
+ // Sort builds by start time (newest first)
83
+ builds.sort((a, b) => b.startTime.getTime() - a.startTime.getTime());
84
+ const lastBuild = builds[0];
85
+ const buildHistory = builds.slice(0, 10); // Last 10 builds
86
+ const successRate = this.calculateSuccessRate(builds);
87
+ const averageDuration = this.calculateAverageDuration(builds);
88
+ const isHealthy = this.determinePackageHealth(builds);
89
+ const issues = this.identifyIssues(builds);
90
+ const status = {
91
+ packageName,
92
+ lastBuild,
93
+ buildHistory,
94
+ successRate,
95
+ averageDuration,
96
+ lastCommit: lastBuild.commit,
97
+ lastCommitDate: lastBuild.startTime,
98
+ branch: lastBuild.branch,
99
+ isHealthy,
100
+ issues,
101
+ };
102
+ this.setCache(cacheKey, status);
103
+ return status;
104
+ }
105
+ catch (error) {
106
+ console.error(`Error fetching CI status for ${packageName}:`, error);
107
+ return null;
108
+ }
109
+ }
110
+ /**
111
+ * Get overall monorepo CI status
112
+ */
113
+ async getMonorepoStatus(packages) {
114
+ const cacheKey = 'monorepo-ci-status';
115
+ const cached = this.getFromCache(cacheKey);
116
+ if (cached) {
117
+ return cached;
118
+ }
119
+ const packageStatuses = [];
120
+ const allBuilds = [];
121
+ let totalTests = 0;
122
+ let passedTests = 0;
123
+ let failedTests = 0;
124
+ const packageCoverage = {};
125
+ // Get status for each package
126
+ for (const pkg of packages) {
127
+ const status = await this.getPackageStatus(pkg.name);
128
+ if (status) {
129
+ packageStatuses.push(status);
130
+ allBuilds.push(...status.buildHistory);
131
+ // Aggregate test results
132
+ if (status.lastBuild?.tests) {
133
+ totalTests += status.lastBuild.tests.total;
134
+ passedTests += status.lastBuild.tests.passed;
135
+ failedTests += status.lastBuild.tests.failed;
136
+ }
137
+ // Aggregate coverage
138
+ if (status.lastBuild?.coverage) {
139
+ packageCoverage[pkg.name] = status.lastBuild.coverage.percentage;
140
+ }
141
+ }
142
+ }
143
+ // Calculate overall metrics
144
+ const totalPackages = packages.length;
145
+ const healthyPackages = packageStatuses.filter(s => s.isHealthy).length;
146
+ const warningPackages = packageStatuses.filter(s => !s.isHealthy && s.issues.length < 3).length;
147
+ const errorPackages = packageStatuses.filter(s => !s.isHealthy && s.issues.length >= 3).length;
148
+ const overallHealth = (healthyPackages / totalPackages) * 100;
149
+ // Sort builds by time
150
+ allBuilds.sort((a, b) => b.startTime.getTime() - a.startTime.getTime());
151
+ const recentBuilds = allBuilds.slice(0, 20);
152
+ const failedBuilds = allBuilds.filter(b => b.status === 'failed');
153
+ // Calculate overall coverage
154
+ const coverageValues = Object.values(packageCoverage);
155
+ const overallCoverage = coverageValues.length > 0
156
+ ? coverageValues.reduce((sum, val) => sum + val, 0) /
157
+ coverageValues.length
158
+ : 0;
159
+ const status = {
160
+ totalPackages,
161
+ healthyPackages,
162
+ warningPackages,
163
+ errorPackages,
164
+ overallHealth,
165
+ packages: packageStatuses,
166
+ recentBuilds,
167
+ failedBuilds,
168
+ coverage: {
169
+ overall: overallCoverage,
170
+ packages: packageCoverage,
171
+ },
172
+ tests: {
173
+ total: totalTests,
174
+ passed: passedTests,
175
+ failed: failedTests,
176
+ successRate: totalTests > 0 ? (passedTests / totalTests) * 100 : 0,
177
+ },
178
+ };
179
+ this.setCache(cacheKey, status);
180
+ return status;
181
+ }
182
+ /**
183
+ * Fetch builds from a specific CI provider
184
+ */
185
+ async fetchBuildsFromProvider(_provider, packageName) {
186
+ // This is a mock implementation
187
+ // In a real implementation, you would make API calls to the CI provider
188
+ const mockBuilds = [
189
+ {
190
+ id: `build-${Date.now()}-1`,
191
+ status: 'success',
192
+ branch: 'main',
193
+ commit: 'abc1234',
194
+ commitMessage: `feat: update ${packageName}`,
195
+ author: 'developer@example.com',
196
+ startTime: new Date(Date.now() - 1000 * 60 * 30), // 30 minutes ago
197
+ endTime: new Date(Date.now() - 1000 * 60 * 25), // 25 minutes ago
198
+ duration: 5 * 60 * 1000, // 5 minutes
199
+ url: `https://ci.example.com/builds/build-${Date.now()}-1`,
200
+ packageName,
201
+ workflowName: 'Build and Test',
202
+ jobName: 'test',
203
+ steps: [
204
+ {
205
+ name: 'Install dependencies',
206
+ status: 'success',
207
+ duration: 30000,
208
+ },
209
+ {
210
+ name: 'Run tests',
211
+ status: 'success',
212
+ duration: 120000,
213
+ },
214
+ {
215
+ name: 'Build package',
216
+ status: 'success',
217
+ duration: 60000,
218
+ },
219
+ ],
220
+ coverage: {
221
+ percentage: 85,
222
+ lines: 1000,
223
+ functions: 50,
224
+ branches: 200,
225
+ statements: 1200,
226
+ },
227
+ tests: {
228
+ total: 150,
229
+ passed: 145,
230
+ failed: 0,
231
+ skipped: 5,
232
+ duration: 120000,
233
+ suites: [
234
+ {
235
+ name: 'Unit Tests',
236
+ status: 'pass',
237
+ tests: [],
238
+ duration: 80000,
239
+ },
240
+ {
241
+ name: 'Integration Tests',
242
+ status: 'pass',
243
+ tests: [],
244
+ duration: 40000,
245
+ },
246
+ ],
247
+ },
248
+ },
249
+ {
250
+ id: `build-${Date.now()}-2`,
251
+ status: 'failed',
252
+ branch: 'feature/new-feature',
253
+ commit: 'def5678',
254
+ commitMessage: `fix: resolve issue in ${packageName}`,
255
+ author: 'developer@example.com',
256
+ startTime: new Date(Date.now() - 1000 * 60 * 60 * 2), // 2 hours ago
257
+ endTime: new Date(Date.now() - 1000 * 60 * 60 * 1.5), // 1.5 hours ago
258
+ duration: 30 * 60 * 1000, // 30 minutes
259
+ url: `https://ci.example.com/builds/build-${Date.now()}-2`,
260
+ packageName,
261
+ workflowName: 'Build and Test',
262
+ jobName: 'test',
263
+ steps: [
264
+ {
265
+ name: 'Install dependencies',
266
+ status: 'success',
267
+ duration: 30000,
268
+ },
269
+ {
270
+ name: 'Run tests',
271
+ status: 'failed',
272
+ duration: 120000,
273
+ error: 'Test suite failed with 3 failing tests',
274
+ },
275
+ ],
276
+ tests: {
277
+ total: 150,
278
+ passed: 147,
279
+ failed: 3,
280
+ skipped: 0,
281
+ duration: 120000,
282
+ suites: [
283
+ {
284
+ name: 'Unit Tests',
285
+ status: 'pass',
286
+ tests: [],
287
+ duration: 80000,
288
+ },
289
+ {
290
+ name: 'Integration Tests',
291
+ status: 'fail',
292
+ tests: [],
293
+ duration: 40000,
294
+ },
295
+ ],
296
+ },
297
+ },
298
+ ];
299
+ return mockBuilds;
300
+ }
301
+ /**
302
+ * Calculate success rate from builds
303
+ */
304
+ calculateSuccessRate(builds) {
305
+ if (builds.length === 0)
306
+ return 0;
307
+ const successfulBuilds = builds.filter(b => b.status === 'success').length;
308
+ return (successfulBuilds / builds.length) * 100;
309
+ }
310
+ /**
311
+ * Calculate average build duration
312
+ */
313
+ calculateAverageDuration(builds) {
314
+ if (builds.length === 0)
315
+ return 0;
316
+ const completedBuilds = builds.filter(b => b.duration !== undefined);
317
+ if (completedBuilds.length === 0)
318
+ return 0;
319
+ const totalDuration = completedBuilds.reduce((sum, b) => sum + (b.duration || 0), 0);
320
+ return totalDuration / completedBuilds.length;
321
+ }
322
+ /**
323
+ * Determine if a package is healthy based on CI results
324
+ */
325
+ determinePackageHealth(builds) {
326
+ if (builds.length === 0)
327
+ return true;
328
+ const recentBuilds = builds.slice(0, 5); // Last 5 builds
329
+ const successRate = this.calculateSuccessRate(recentBuilds);
330
+ return successRate >= 80; // 80% success rate threshold
331
+ }
332
+ /**
333
+ * Identify issues from build results
334
+ */
335
+ identifyIssues(builds) {
336
+ const issues = [];
337
+ if (builds.length === 0)
338
+ return issues;
339
+ const recentBuilds = builds.slice(0, 3); // Last 3 builds
340
+ const successRate = this.calculateSuccessRate(recentBuilds);
341
+ if (successRate < 50) {
342
+ issues.push('High failure rate in recent builds');
343
+ }
344
+ const failedBuilds = recentBuilds.filter(b => b.status === 'failed');
345
+ for (const build of failedBuilds) {
346
+ const failedSteps = build.steps.filter(s => s.status === 'failed');
347
+ for (const step of failedSteps) {
348
+ if (step.error) {
349
+ issues.push(`Build step '${step.name}' failed: ${step.error}`);
350
+ }
351
+ }
352
+ }
353
+ // Check for long build times
354
+ const avgDuration = this.calculateAverageDuration(recentBuilds);
355
+ if (avgDuration > 10 * 60 * 1000) {
356
+ // 10 minutes
357
+ issues.push('Builds are taking longer than expected');
358
+ }
359
+ return issues;
360
+ }
361
+ /**
362
+ * Get cache value if not expired
363
+ */
364
+ getFromCache(key) {
365
+ const cached = this.cache.get(key);
366
+ if (cached && Date.now() - cached.timestamp < this.cacheExpiry) {
367
+ return cached.data;
368
+ }
369
+ return null;
370
+ }
371
+ /**
372
+ * Set cache value with timestamp
373
+ */
374
+ setCache(key, data) {
375
+ this.cache.set(key, {
376
+ data,
377
+ timestamp: Date.now(),
378
+ });
379
+ }
380
+ /**
381
+ * Clear the cache
382
+ */
383
+ clearCache() {
384
+ this.cache.clear();
385
+ }
386
+ /**
387
+ * Get build logs for a specific build
388
+ */
389
+ async getBuildLogs(buildId, providerName) {
390
+ // Mock implementation
391
+ return `Build logs for ${buildId} from ${providerName}\n\nStep 1: Install dependencies\n✓ Dependencies installed successfully\n\nStep 2: Run tests\n✓ All tests passed\n\nStep 3: Build package\n✓ Package built successfully`;
392
+ }
393
+ /**
394
+ * Trigger a new build for a package
395
+ */
396
+ async triggerBuild(packageName, providerName, branch = 'main') {
397
+ try {
398
+ // Mock implementation
399
+ const buildId = `build-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
400
+ console.log(`Triggering build for ${packageName} on ${branch} via ${providerName}`);
401
+ return {
402
+ success: true,
403
+ buildId,
404
+ };
405
+ }
406
+ catch (error) {
407
+ return {
408
+ success: false,
409
+ error: error instanceof Error ? error.message : 'Unknown error',
410
+ };
411
+ }
412
+ }
413
+ /**
414
+ * Get build artifacts
415
+ */
416
+ async getBuildArtifacts(buildId, _providerName) {
417
+ // Mock implementation
418
+ return [
419
+ {
420
+ name: 'coverage-report.html',
421
+ type: 'coverage',
422
+ size: 1024 * 50, // 50KB
423
+ url: `https://ci.example.com/artifacts/${buildId}/coverage-report.html`,
424
+ },
425
+ {
426
+ name: 'test-results.xml',
427
+ type: 'test',
428
+ size: 1024 * 10, // 10KB
429
+ url: `https://ci.example.com/artifacts/${buildId}/test-results.xml`,
430
+ },
431
+ ];
432
+ }
433
+ }
434
+ exports.CIStatusManager = CIStatusManager;
435
+ // Export default instance
436
+ exports.ciStatusManager = new CIStatusManager();
437
+ // Export convenience functions
438
+ async function getPackageCIStatus(packageName) {
439
+ return exports.ciStatusManager.getPackageStatus(packageName);
440
+ }
441
+ async function getMonorepoCIStatus(packages) {
442
+ return exports.ciStatusManager.getMonorepoStatus(packages);
443
+ }
444
+ async function triggerPackageBuild(packageName, providerName, branch) {
445
+ return exports.ciStatusManager.triggerBuild(packageName, providerName, branch);
446
+ }
@@ -0,0 +1,237 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getCommits = getCommits;
37
+ exports.storePackage = storePackage;
38
+ exports.storeCommits = storeCommits;
39
+ exports.getPackageDependenciesInfo = getPackageDependenciesInfo;
40
+ exports.storeDependencies = storeDependencies;
41
+ // dotenv.config({ path: path.resolve(__dirname, '../.env') });
42
+ // Fallback import to handle environments where PrismaClient isn't a named export
43
+ const PrismaPkg = __importStar(require("@prisma/client"));
44
+ const PrismaClient = PrismaPkg.PrismaClient || PrismaPkg.default || PrismaPkg;
45
+ // Provide a fallback reference to the Prisma namespace so errors like
46
+ // Prisma.PrismaClientKnownRequestError can be referenced safely.
47
+ const Prisma = PrismaPkg.Prisma || PrismaPkg.PrismaClient?.Prisma || PrismaPkg.default?.Prisma || PrismaPkg;
48
+ const config_loader_1 = require("../config-loader");
49
+ // Default settings
50
+ const DEFAULT_PORT = 4000;
51
+ const port = config_loader_1.appConfig.server.port ?? DEFAULT_PORT; //Default port
52
+ const host = config_loader_1.appConfig.server.host ?? 'localhost'; //Default host
53
+ const prisma = new PrismaClient();
54
+ const API_BASE = `http://${host}:${port}/api`;
55
+ async function getCommits(path) {
56
+ const res = await fetch(API_BASE + `/commits/` + encodeURIComponent(path));
57
+ if (!res.ok)
58
+ throw new Error('Failed to fetch commits');
59
+ return (await res.json());
60
+ }
61
+ async function storeCommits(packageName, commits) {
62
+ console.log('💾 Storing commits for:' + packageName);
63
+ // Create or update dependencies
64
+ for (const commit of commits) {
65
+ try {
66
+ await prisma.commit.upsert({
67
+ where: {
68
+ hash: commit.hash,
69
+ },
70
+ update: {
71
+ message: commit.message,
72
+ author: commit.author,
73
+ date: commit.date,
74
+ type: commit.type,
75
+ },
76
+ create: {
77
+ hash: commit.hash,
78
+ message: commit.message,
79
+ author: commit.author,
80
+ date: commit.date,
81
+ type: commit.type,
82
+ packageName: packageName,
83
+ },
84
+ });
85
+ }
86
+ catch (e) {
87
+ const err = e;
88
+ if (err instanceof Prisma.PrismaClientKnownRequestError &&
89
+ err.code === 'P2002') {
90
+ // Handle unique constraint violation (e.g., commit already exists)
91
+ console.warn(`Skipping commit: ${commit.hash} (Depenndency already exists)`);
92
+ }
93
+ else {
94
+ // Handle any other unexpected errors
95
+ console.error(`Failed to store commit: ${commit.hash}`, err);
96
+ }
97
+ }
98
+ }
99
+ }
100
+ /**
101
+ * Store packages in database
102
+ */
103
+ async function storePackage(pkg) {
104
+ try {
105
+ // Create or update package
106
+ await prisma.package.upsert({
107
+ where: { name: pkg.name },
108
+ update: {
109
+ version: pkg.version,
110
+ type: pkg.type,
111
+ path: pkg.path,
112
+ description: pkg.description,
113
+ license: pkg.license,
114
+ repository: JSON.stringify(pkg.repository),
115
+ scripts: JSON.stringify(pkg.scripts),
116
+ lastUpdated: new Date(),
117
+ },
118
+ create: {
119
+ // Timestamps
120
+ createdAt: new Date(),
121
+ lastUpdated: new Date(),
122
+ // Key Metrics and Relationships
123
+ dependencies: JSON.stringify(pkg.dependencies), // The total number of direct dependencies (12 in your example)
124
+ // Manual Serialization Required: Stores a JSON array string of maintainers, e.g., '["team-frontend"]'
125
+ maintainers: pkg.maintainers.join(','),
126
+ // Manual Serialization Required: Stores a JSON array string of tags, e.g., '["core", "ui"]'
127
+ // Manual Serialization Required: Stores the scripts object as a JSON string
128
+ // Example: '{"dev": "vite", "build": "tsc && vite build"}'
129
+ scripts: JSON.stringify(pkg.scripts),
130
+ // Dependency Lists (Manual Serialization Required)
131
+ // Stores a JSON array string of dependencies.
132
+ // dependenciesList: JSON.stringify(pkg.dependenciesList),
133
+ devDependencies: JSON.stringify(pkg.devDependencies),
134
+ peerDependencies: JSON.stringify(pkg.peerDependencies),
135
+ name: pkg.name,
136
+ version: pkg.version,
137
+ type: pkg.type,
138
+ path: pkg.path,
139
+ description: pkg.description ?? '',
140
+ license: pkg.license ?? '',
141
+ repository: JSON.stringify(pkg.repository),
142
+ status: '',
143
+ },
144
+ });
145
+ const commits = await getCommits(pkg.path ?? '');
146
+ if (commits.length) {
147
+ await storeCommits(pkg.name, commits);
148
+ }
149
+ const dependenciesInfo = getPackageDependenciesInfo(pkg);
150
+ if (dependenciesInfo.length) {
151
+ await storeDependencies(pkg.name, dependenciesInfo);
152
+ }
153
+ }
154
+ catch (error) {
155
+ console.warn(`⚠️ Failed to store report for ${pkg.name}:`, error);
156
+ }
157
+ }
158
+ /**
159
+ * Get package dependencies
160
+ */
161
+ function getPackageDependenciesInfo(pkg) {
162
+ const allDeps = [];
163
+ Object.keys(pkg.dependencies || {}).forEach(depName => {
164
+ allDeps.push({
165
+ name: depName,
166
+ version: pkg.dependencies[depName],
167
+ type: 'dependency',
168
+ latest: '',
169
+ outdated: false,
170
+ });
171
+ });
172
+ Object.keys(pkg.devDependencies || {}).forEach(depName => {
173
+ allDeps.push({
174
+ name: depName,
175
+ version: pkg.devDependencies[depName],
176
+ type: 'devDependency',
177
+ latest: '',
178
+ outdated: false,
179
+ });
180
+ });
181
+ Object.keys(pkg.peerDependencies || {}).forEach(depName => {
182
+ allDeps.push({
183
+ name: depName,
184
+ version: pkg.peerDependencies[depName],
185
+ type: 'peerDependency',
186
+ latest: '',
187
+ outdated: false,
188
+ });
189
+ });
190
+ return allDeps;
191
+ }
192
+ /**
193
+ * Store dependencies in database
194
+ */
195
+ async function storeDependencies(packageName, dependencies) {
196
+ console.log('💾 Storing Dependencies for:' + packageName);
197
+ // Create or update dependencies
198
+ for (const dep of dependencies) {
199
+ try {
200
+ await prisma.dependencyInfo.upsert({
201
+ where: {
202
+ name_packageName: {
203
+ // This refers to the composite unique constraint
204
+ name: dep.name,
205
+ packageName,
206
+ },
207
+ },
208
+ update: {
209
+ version: dep.version,
210
+ type: dep.type,
211
+ latest: dep.latest,
212
+ outdated: dep.outdated,
213
+ },
214
+ create: {
215
+ name: dep.name,
216
+ version: dep.version,
217
+ type: dep.type,
218
+ latest: dep.latest,
219
+ outdated: dep.outdated,
220
+ packageName: packageName,
221
+ },
222
+ });
223
+ }
224
+ catch (e) {
225
+ const err = e;
226
+ if (err instanceof Prisma.PrismaClientKnownRequestError &&
227
+ err.code === 'P2002') {
228
+ // Handle unique constraint violation (e.g., depedency already exists)
229
+ console.warn(`Skipping dependency: ${dep.name} (Depenndency already exists)`);
230
+ }
231
+ else {
232
+ // Handle any other unexpected errors
233
+ console.error(`Failed to store dependency: ${dep.name}`, err);
234
+ }
235
+ }
236
+ }
237
+ }