treliq 0.4.0

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 (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +340 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +540 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/core/cache.d.ts +29 -0
  8. package/dist/core/cache.d.ts.map +1 -0
  9. package/dist/core/cache.js +64 -0
  10. package/dist/core/cache.js.map +1 -0
  11. package/dist/core/concurrency.d.ts +16 -0
  12. package/dist/core/concurrency.d.ts.map +1 -0
  13. package/dist/core/concurrency.js +60 -0
  14. package/dist/core/concurrency.js.map +1 -0
  15. package/dist/core/db.d.ts +127 -0
  16. package/dist/core/db.d.ts.map +1 -0
  17. package/dist/core/db.js +490 -0
  18. package/dist/core/db.js.map +1 -0
  19. package/dist/core/dedup.d.ts +18 -0
  20. package/dist/core/dedup.d.ts.map +1 -0
  21. package/dist/core/dedup.js +159 -0
  22. package/dist/core/dedup.js.map +1 -0
  23. package/dist/core/graphql.d.ts +30 -0
  24. package/dist/core/graphql.d.ts.map +1 -0
  25. package/dist/core/graphql.js +243 -0
  26. package/dist/core/graphql.js.map +1 -0
  27. package/dist/core/notifications.d.ts +37 -0
  28. package/dist/core/notifications.d.ts.map +1 -0
  29. package/dist/core/notifications.js +174 -0
  30. package/dist/core/notifications.js.map +1 -0
  31. package/dist/core/provider.d.ts +45 -0
  32. package/dist/core/provider.d.ts.map +1 -0
  33. package/dist/core/provider.js +147 -0
  34. package/dist/core/provider.js.map +1 -0
  35. package/dist/core/ratelimit.d.ts +40 -0
  36. package/dist/core/ratelimit.d.ts.map +1 -0
  37. package/dist/core/ratelimit.js +77 -0
  38. package/dist/core/ratelimit.js.map +1 -0
  39. package/dist/core/reputation.d.ts +16 -0
  40. package/dist/core/reputation.d.ts.map +1 -0
  41. package/dist/core/reputation.js +59 -0
  42. package/dist/core/reputation.js.map +1 -0
  43. package/dist/core/scanner.d.ts +58 -0
  44. package/dist/core/scanner.d.ts.map +1 -0
  45. package/dist/core/scanner.js +635 -0
  46. package/dist/core/scanner.js.map +1 -0
  47. package/dist/core/scoring.d.ts +36 -0
  48. package/dist/core/scoring.d.ts.map +1 -0
  49. package/dist/core/scoring.js +360 -0
  50. package/dist/core/scoring.js.map +1 -0
  51. package/dist/core/types.d.ts +89 -0
  52. package/dist/core/types.d.ts.map +1 -0
  53. package/dist/core/types.js +6 -0
  54. package/dist/core/types.js.map +1 -0
  55. package/dist/core/vectorstore.d.ts +42 -0
  56. package/dist/core/vectorstore.d.ts.map +1 -0
  57. package/dist/core/vectorstore.js +149 -0
  58. package/dist/core/vectorstore.js.map +1 -0
  59. package/dist/core/vision.d.ts +16 -0
  60. package/dist/core/vision.d.ts.map +1 -0
  61. package/dist/core/vision.js +41 -0
  62. package/dist/core/vision.js.map +1 -0
  63. package/dist/index.d.ts +21 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +34 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/server/app.d.ts +21 -0
  68. package/dist/server/app.d.ts.map +1 -0
  69. package/dist/server/app.js +284 -0
  70. package/dist/server/app.js.map +1 -0
  71. package/dist/server/index.d.ts +29 -0
  72. package/dist/server/index.d.ts.map +1 -0
  73. package/dist/server/index.js +117 -0
  74. package/dist/server/index.js.map +1 -0
  75. package/dist/server/scheduler.d.ts +26 -0
  76. package/dist/server/scheduler.d.ts.map +1 -0
  77. package/dist/server/scheduler.js +136 -0
  78. package/dist/server/scheduler.js.map +1 -0
  79. package/dist/server/sse.d.ts +33 -0
  80. package/dist/server/sse.d.ts.map +1 -0
  81. package/dist/server/sse.js +80 -0
  82. package/dist/server/sse.js.map +1 -0
  83. package/dist/server/webhooks.d.ts +23 -0
  84. package/dist/server/webhooks.d.ts.map +1 -0
  85. package/dist/server/webhooks.js +175 -0
  86. package/dist/server/webhooks.js.map +1 -0
  87. package/package.json +84 -0
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ /**
3
+ * Treliq Server Module
4
+ *
5
+ * Provides REST API, webhook handling, and scheduled scanning
6
+ * for continuous PR monitoring and triage.
7
+ *
8
+ * Usage:
9
+ * import { startServer } from './server';
10
+ * await startServer({ port: 8000, ... });
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.startScheduler = exports.registerWebhooks = exports.createServer = void 0;
14
+ exports.startServer = startServer;
15
+ var app_1 = require("./app");
16
+ Object.defineProperty(exports, "createServer", { enumerable: true, get: function () { return app_1.createServer; } });
17
+ var webhooks_1 = require("./webhooks");
18
+ Object.defineProperty(exports, "registerWebhooks", { enumerable: true, get: function () { return webhooks_1.registerWebhooks; } });
19
+ var scheduler_1 = require("./scheduler");
20
+ Object.defineProperty(exports, "startScheduler", { enumerable: true, get: function () { return scheduler_1.startScheduler; } });
21
+ const app_2 = require("./app");
22
+ const scheduler_2 = require("./scheduler");
23
+ const notifications_1 = require("../core/notifications");
24
+ const db_1 = require("../core/db");
25
+ /**
26
+ * Start Treliq server with all components:
27
+ * - REST API
28
+ * - Webhook handler (if webhookSecret provided)
29
+ * - Scheduler (if scheduledRepos provided)
30
+ * - Notifications (if webhook URLs provided)
31
+ */
32
+ async function startServer(config) {
33
+ console.error('🚀 Starting Treliq Server...\n');
34
+ // Initialize notifications if configured
35
+ let notifications;
36
+ if (config.slackWebhook || config.discordWebhook) {
37
+ notifications = new notifications_1.NotificationDispatcher({
38
+ slackWebhook: config.slackWebhook,
39
+ discordWebhook: config.discordWebhook,
40
+ });
41
+ console.error('📢 Notifications enabled');
42
+ if (config.slackWebhook)
43
+ console.error(' - Slack webhook configured');
44
+ if (config.discordWebhook)
45
+ console.error(' - Discord webhook configured');
46
+ }
47
+ // Create and start Fastify server
48
+ const fastify = await (0, app_2.createServer)(config);
49
+ try {
50
+ await fastify.listen({
51
+ port: config.port,
52
+ host: config.host,
53
+ });
54
+ console.error(`\n✅ Server listening on http://${config.host}:${config.port}`);
55
+ console.error(` Health check: http://${config.host}:${config.port}/health`);
56
+ if (config.webhookSecret) {
57
+ console.error(` Webhook endpoint: http://${config.host}:${config.port}/webhooks`);
58
+ }
59
+ }
60
+ catch (error) {
61
+ console.error('❌ Failed to start server:', error);
62
+ throw error;
63
+ }
64
+ // Start scheduler if configured
65
+ let schedulerHandle;
66
+ if (config.scheduledRepos && config.scheduledRepos.length > 0) {
67
+ if (!config.cronExpression) {
68
+ console.error('⚠️ scheduledRepos provided but no cronExpression - scheduler not started');
69
+ }
70
+ else {
71
+ try {
72
+ const db = new db_1.TreliqDB(config.dbPath);
73
+ const schedulerConfig = {
74
+ cronExpression: config.cronExpression,
75
+ repos: config.scheduledRepos,
76
+ treliqConfig: config.treliqConfig,
77
+ db,
78
+ notifications,
79
+ };
80
+ schedulerHandle = (0, scheduler_2.startScheduler)(schedulerConfig);
81
+ }
82
+ catch (error) {
83
+ console.error('❌ Failed to start scheduler:', error.message);
84
+ // Continue without scheduler
85
+ }
86
+ }
87
+ }
88
+ // Log startup summary
89
+ console.error('\n📋 Server Configuration:');
90
+ console.error(` Port: ${config.port}`);
91
+ console.error(` Host: ${config.host}`);
92
+ console.error(` Database: ${config.dbPath}`);
93
+ console.error(` Repository: ${config.treliqConfig.repo}`);
94
+ console.error(` Webhooks: ${config.webhookSecret ? 'Enabled' : 'Disabled'}`);
95
+ console.error(` Scheduler: ${schedulerHandle ? 'Enabled' : 'Disabled'}`);
96
+ console.error(` Notifications: ${notifications?.hasChannels ? 'Enabled' : 'Disabled'}`);
97
+ console.error('\n🎯 Server ready to accept requests\n');
98
+ // Handle graceful shutdown
99
+ const shutdown = async (signal) => {
100
+ console.error(`\n🛑 Received ${signal}, shutting down...`);
101
+ if (schedulerHandle) {
102
+ schedulerHandle.stop();
103
+ }
104
+ try {
105
+ await fastify.close();
106
+ console.error('✅ Server shutdown complete');
107
+ process.exit(0);
108
+ }
109
+ catch (error) {
110
+ console.error('❌ Error during shutdown:', error);
111
+ process.exit(1);
112
+ }
113
+ };
114
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
115
+ process.on('SIGINT', () => shutdown('SIGINT'));
116
+ }
117
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Scheduled Repository Scanner for Treliq
3
+ *
4
+ * Uses node-cron to periodically scan multiple repositories,
5
+ * save results to database, and optionally send notifications.
6
+ */
7
+ import { TreliqDB } from '../core/db';
8
+ import { NotificationDispatcher } from '../core/notifications';
9
+ import type { TreliqConfig } from '../core/types';
10
+ export interface SchedulerConfig {
11
+ cronExpression: string;
12
+ repos: string[];
13
+ treliqConfig: TreliqConfig;
14
+ db: TreliqDB;
15
+ notifications?: NotificationDispatcher;
16
+ }
17
+ export interface SchedulerHandle {
18
+ stop: () => void;
19
+ isRunning: () => boolean;
20
+ }
21
+ /**
22
+ * Start a cron scheduler that scans repositories on schedule
23
+ * Returns a handle to stop the scheduler
24
+ */
25
+ export declare function startScheduler(config: SchedulerConfig): SchedulerHandle;
26
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/server/scheduler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,EAAE,YAAY,CAAC;IAC3B,EAAE,EAAE,QAAQ,CAAC;IACb,aAAa,CAAC,EAAE,sBAAsB,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAsDvE"}
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ /**
3
+ * Scheduled Repository Scanner for Treliq
4
+ *
5
+ * Uses node-cron to periodically scan multiple repositories,
6
+ * save results to database, and optionally send notifications.
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.startScheduler = startScheduler;
13
+ const node_cron_1 = __importDefault(require("node-cron"));
14
+ const scanner_1 = require("../core/scanner");
15
+ const db_1 = require("../core/db");
16
+ /**
17
+ * Start a cron scheduler that scans repositories on schedule
18
+ * Returns a handle to stop the scheduler
19
+ */
20
+ function startScheduler(config) {
21
+ if (config.repos.length === 0) {
22
+ throw new Error('No repositories configured for scheduled scanning');
23
+ }
24
+ // Validate cron expression
25
+ if (!node_cron_1.default.validate(config.cronExpression)) {
26
+ throw new Error(`Invalid cron expression: ${config.cronExpression}`);
27
+ }
28
+ console.error(`⏰ Scheduler configured for ${config.repos.length} repositories`);
29
+ console.error(` Schedule: ${config.cronExpression}`);
30
+ console.error(` Repositories: ${config.repos.join(', ')}`);
31
+ let isRunning = false;
32
+ const task = node_cron_1.default.schedule(config.cronExpression, async () => {
33
+ if (isRunning) {
34
+ console.error('⏭️ Previous scan still running, skipping this cycle');
35
+ return;
36
+ }
37
+ isRunning = true;
38
+ const startTime = Date.now();
39
+ console.error(`\n🕐 ${new Date().toISOString()} - Starting scheduled scan...`);
40
+ try {
41
+ await scanAllRepositories(config);
42
+ const duration = ((Date.now() - startTime) / 1000).toFixed(1);
43
+ console.error(`✅ Scheduled scan complete (${duration}s)\n`);
44
+ }
45
+ catch (error) {
46
+ console.error(`❌ Scheduled scan failed:`, error.message);
47
+ }
48
+ finally {
49
+ isRunning = false;
50
+ }
51
+ }, {
52
+ timezone: 'UTC',
53
+ });
54
+ task.start();
55
+ console.error('✅ Scheduler started');
56
+ return {
57
+ stop: () => {
58
+ task.stop();
59
+ console.error('🛑 Scheduler stopped');
60
+ },
61
+ isRunning: () => isRunning,
62
+ };
63
+ }
64
+ /**
65
+ * Scan all configured repositories sequentially
66
+ */
67
+ async function scanAllRepositories(config) {
68
+ const results = {
69
+ successful: 0,
70
+ failed: 0,
71
+ totalPRs: 0,
72
+ totalSpam: 0,
73
+ };
74
+ for (const repo of config.repos) {
75
+ try {
76
+ console.error(`\n📡 Scanning ${repo}...`);
77
+ const scanConfig = {
78
+ ...config.treliqConfig,
79
+ repo,
80
+ dbPath: config.db instanceof db_1.TreliqDB ? config.db.db.name : undefined,
81
+ };
82
+ const scanner = new scanner_1.TreliqScanner(scanConfig);
83
+ const result = await scanner.scan();
84
+ results.successful++;
85
+ results.totalPRs += result.totalPRs;
86
+ results.totalSpam += result.spamCount;
87
+ // Send scan completion notification
88
+ if (config.notifications?.hasChannels) {
89
+ await config.notifications.send({
90
+ type: 'scan_complete',
91
+ repo,
92
+ message: `Scanned ${result.totalPRs} PRs, found ${result.spamCount} spam, ${result.duplicateClusters.length} duplicate clusters`,
93
+ });
94
+ }
95
+ // Send notifications for high-priority PRs (score >= 90)
96
+ if (config.notifications?.hasChannels) {
97
+ const highPriorityPRs = result.rankedPRs.filter(pr => pr.totalScore >= 90 && !pr.isSpam);
98
+ for (const pr of highPriorityPRs.slice(0, 3)) {
99
+ // Limit to top 3 to avoid spam
100
+ await config.notifications.send({
101
+ type: 'high_priority_pr',
102
+ repo,
103
+ message: `High priority PR: ${pr.title}`,
104
+ prNumber: pr.number,
105
+ score: Math.round(pr.totalScore),
106
+ url: `https://github.com/${repo}/pull/${pr.number}`,
107
+ });
108
+ }
109
+ }
110
+ }
111
+ catch (error) {
112
+ console.error(`❌ Failed to scan ${repo}:`, error.message);
113
+ results.failed++;
114
+ // Send error notification
115
+ if (config.notifications?.hasChannels) {
116
+ try {
117
+ await config.notifications.send({
118
+ type: 'spam_detected', // Reuse spam type for errors (red color)
119
+ repo,
120
+ message: `Scan failed: ${error.message}`,
121
+ });
122
+ }
123
+ catch (notifError) {
124
+ console.error(`⚠️ Failed to send error notification:`, notifError.message);
125
+ }
126
+ }
127
+ }
128
+ }
129
+ // Log summary
130
+ console.error(`\n📊 Scan Summary:`);
131
+ console.error(` Successful: ${results.successful}`);
132
+ console.error(` Failed: ${results.failed}`);
133
+ console.error(` Total PRs: ${results.totalPRs}`);
134
+ console.error(` Total Spam: ${results.totalSpam}`);
135
+ }
136
+ //# sourceMappingURL=scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/server/scheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * SSE (Server-Sent Events) Broadcaster for Treliq
3
+ *
4
+ * Provides real-time updates to connected dashboard clients.
5
+ * No additional dependencies — uses native HTTP streaming via Fastify reply.
6
+ */
7
+ import type { FastifyReply } from 'fastify';
8
+ export type SSEEvent = 'scan_start' | 'scan_complete' | 'pr_scored' | 'pr_closed';
9
+ export declare class SSEBroadcaster {
10
+ private clients;
11
+ /**
12
+ * Register a new SSE client connection.
13
+ * Sets appropriate headers and handles cleanup on disconnect.
14
+ */
15
+ addClient(reply: FastifyReply): void;
16
+ /**
17
+ * Broadcast an event to all connected clients.
18
+ */
19
+ broadcast(event: SSEEvent, data: any): void;
20
+ /**
21
+ * Send a keepalive ping to all clients (prevents proxy timeouts).
22
+ */
23
+ ping(): void;
24
+ /**
25
+ * Number of connected clients.
26
+ */
27
+ get clientCount(): number;
28
+ /**
29
+ * Close all client connections.
30
+ */
31
+ closeAll(): void;
32
+ }
33
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/server/sse.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,eAAe,GAAG,WAAW,GAAG,WAAW,CAAC;AAElF,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAA2B;IAE1C;;;OAGG;IACH,SAAS,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAmBpC;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAa3C;;OAEG;IACH,IAAI,IAAI,IAAI;IAWZ;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;OAEG;IACH,QAAQ,IAAI,IAAI;CAQjB"}
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ /**
3
+ * SSE (Server-Sent Events) Broadcaster for Treliq
4
+ *
5
+ * Provides real-time updates to connected dashboard clients.
6
+ * No additional dependencies — uses native HTTP streaming via Fastify reply.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.SSEBroadcaster = void 0;
10
+ class SSEBroadcaster {
11
+ clients = new Set();
12
+ /**
13
+ * Register a new SSE client connection.
14
+ * Sets appropriate headers and handles cleanup on disconnect.
15
+ */
16
+ addClient(reply) {
17
+ reply.raw.writeHead(200, {
18
+ 'Content-Type': 'text/event-stream',
19
+ 'Cache-Control': 'no-cache',
20
+ 'Connection': 'keep-alive',
21
+ 'Access-Control-Allow-Origin': '*',
22
+ });
23
+ // Send initial connection event
24
+ reply.raw.write(`event: connected\ndata: ${JSON.stringify({ timestamp: new Date().toISOString() })}\n\n`);
25
+ this.clients.add(reply);
26
+ // Clean up on disconnect
27
+ reply.raw.on('close', () => {
28
+ this.clients.delete(reply);
29
+ });
30
+ }
31
+ /**
32
+ * Broadcast an event to all connected clients.
33
+ */
34
+ broadcast(event, data) {
35
+ const payload = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
36
+ for (const client of this.clients) {
37
+ try {
38
+ client.raw.write(payload);
39
+ }
40
+ catch {
41
+ // Client disconnected — remove
42
+ this.clients.delete(client);
43
+ }
44
+ }
45
+ }
46
+ /**
47
+ * Send a keepalive ping to all clients (prevents proxy timeouts).
48
+ */
49
+ ping() {
50
+ const payload = `: keepalive ${new Date().toISOString()}\n\n`;
51
+ for (const client of this.clients) {
52
+ try {
53
+ client.raw.write(payload);
54
+ }
55
+ catch {
56
+ this.clients.delete(client);
57
+ }
58
+ }
59
+ }
60
+ /**
61
+ * Number of connected clients.
62
+ */
63
+ get clientCount() {
64
+ return this.clients.size;
65
+ }
66
+ /**
67
+ * Close all client connections.
68
+ */
69
+ closeAll() {
70
+ for (const client of this.clients) {
71
+ try {
72
+ client.raw.end();
73
+ }
74
+ catch { /* ignore */ }
75
+ }
76
+ this.clients.clear();
77
+ }
78
+ }
79
+ exports.SSEBroadcaster = SSEBroadcaster;
80
+ //# sourceMappingURL=sse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/server/sse.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAMH,MAAa,cAAc;IACjB,OAAO,GAAG,IAAI,GAAG,EAAgB,CAAC;IAE1C;;;OAGG;IACH,SAAS,CAAC,KAAmB;QAC3B,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;YACvB,cAAc,EAAE,mBAAmB;YACnC,eAAe,EAAE,UAAU;YAC3B,YAAY,EAAE,YAAY;YAC1B,6BAA6B,EAAE,GAAG;SACnC,CAAC,CAAC;QAEH,gCAAgC;QAChC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,2BAA2B,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;QAE1G,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAExB,yBAAyB;QACzB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACzB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,KAAe,EAAE,IAAS;QAClC,MAAM,OAAO,GAAG,UAAU,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;QAErE,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,+BAA+B;gBAC/B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,IAAI;QACF,MAAM,OAAO,GAAG,eAAe,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC;QAC9D,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;YACnB,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QAC1B,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;CACF;AA1ED,wCA0EC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * GitHub Webhook Handler for Treliq
3
+ *
4
+ * Handles incoming webhook events from GitHub:
5
+ * - pull_request.opened: Score new PRs
6
+ * - pull_request.synchronize: Re-score updated PRs
7
+ * - pull_request.closed: Update PR state in DB
8
+ */
9
+ import type { FastifyInstance } from 'fastify';
10
+ import { TreliqDB } from '../core/db';
11
+ import type { TreliqConfig } from '../core/types';
12
+ import type { SSEBroadcaster } from './sse';
13
+ export interface WebhookConfig {
14
+ secret: string;
15
+ treliqConfig: TreliqConfig;
16
+ db: TreliqDB;
17
+ broadcaster?: SSEBroadcaster;
18
+ }
19
+ /**
20
+ * Register webhook endpoint with GitHub signature verification
21
+ */
22
+ export declare function registerWebhooks(fastify: FastifyInstance, config: WebhookConfig): void;
23
+ //# sourceMappingURL=webhooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/server/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAG7E,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,YAAY,CAAC;IAC3B,EAAE,EAAE,QAAQ,CAAC;IACb,WAAW,CAAC,EAAE,cAAc,CAAC;CAC9B;AAwCD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,aAAa,GACpB,IAAI,CA2DN"}
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ /**
3
+ * GitHub Webhook Handler for Treliq
4
+ *
5
+ * Handles incoming webhook events from GitHub:
6
+ * - pull_request.opened: Score new PRs
7
+ * - pull_request.synchronize: Re-score updated PRs
8
+ * - pull_request.closed: Update PR state in DB
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.registerWebhooks = registerWebhooks;
12
+ const crypto_1 = require("crypto");
13
+ const scanner_1 = require("../core/scanner");
14
+ const db_1 = require("../core/db");
15
+ /**
16
+ * Verify GitHub webhook signature using HMAC-SHA256
17
+ */
18
+ function verifySignature(payload, signature, secret) {
19
+ if (!signature)
20
+ return false;
21
+ const hmac = (0, crypto_1.createHmac)('sha256', secret);
22
+ hmac.update(payload, 'utf8');
23
+ const digest = `sha256=${hmac.digest('hex')}`;
24
+ // Constant-time comparison to prevent timing attacks
25
+ if (signature.length !== digest.length)
26
+ return false;
27
+ return signature === digest;
28
+ }
29
+ /**
30
+ * Register webhook endpoint with GitHub signature verification
31
+ */
32
+ function registerWebhooks(fastify, config) {
33
+ fastify.post('/webhooks', {
34
+ config: {
35
+ // Raw body needed for signature verification
36
+ rawBody: true,
37
+ },
38
+ }, async (request, reply) => {
39
+ const signature = request.headers['x-hub-signature-256'];
40
+ const event = request.headers['x-github-event'];
41
+ const deliveryId = request.headers['x-github-delivery'];
42
+ console.error(`📨 Webhook received: ${event} (delivery: ${deliveryId})`);
43
+ // Get raw body for signature verification
44
+ let rawBody;
45
+ try {
46
+ rawBody = JSON.stringify(request.body);
47
+ }
48
+ catch {
49
+ return reply.code(400).send({
50
+ error: 'Invalid JSON payload',
51
+ });
52
+ }
53
+ // Verify signature
54
+ if (!signature || !verifySignature(rawBody, signature, config.secret)) {
55
+ console.error('⚠️ Invalid webhook signature');
56
+ return reply.code(401).send({
57
+ error: 'Invalid signature',
58
+ });
59
+ }
60
+ const payload = request.body;
61
+ // Handle different webhook events
62
+ try {
63
+ if (event === 'pull_request') {
64
+ await handlePullRequestEvent(payload, config);
65
+ return reply.code(200).send({ status: 'processed' });
66
+ }
67
+ else if (event === 'ping') {
68
+ console.error('🏓 Webhook ping received');
69
+ return reply.code(200).send({ status: 'pong' });
70
+ }
71
+ else {
72
+ console.error(`⏭️ Ignoring event: ${event}`);
73
+ return reply.code(200).send({ status: 'ignored' });
74
+ }
75
+ }
76
+ catch (error) {
77
+ console.error(`❌ Webhook processing error:`, error);
78
+ return reply.code(500).send({
79
+ error: 'Webhook processing failed',
80
+ message: error.message,
81
+ });
82
+ }
83
+ });
84
+ console.error('✅ Webhook endpoint registered at POST /webhooks');
85
+ }
86
+ /**
87
+ * Handle pull_request webhook events
88
+ */
89
+ async function handlePullRequestEvent(payload, config) {
90
+ const { action, pull_request, repository } = payload;
91
+ if (!pull_request || !repository) {
92
+ console.error('⚠️ Missing pull_request or repository in payload');
93
+ return;
94
+ }
95
+ const owner = repository.owner.login;
96
+ const repo = repository.name;
97
+ const prNumber = pull_request.number;
98
+ const repoFullName = `${owner}/${repo}`;
99
+ console.error(`📋 Processing ${action} for ${repoFullName}#${prNumber}`);
100
+ const repoId = config.db.upsertRepository(owner, repo);
101
+ try {
102
+ switch (action) {
103
+ case 'opened':
104
+ // New PR opened - score it
105
+ await scorePR(repoFullName, prNumber, config);
106
+ console.error(`✅ Scored new PR ${repoFullName}#${prNumber}`);
107
+ break;
108
+ case 'synchronize':
109
+ // PR updated (new commits) - re-score
110
+ await scorePR(repoFullName, prNumber, config);
111
+ console.error(`✅ Re-scored updated PR ${repoFullName}#${prNumber}`);
112
+ break;
113
+ case 'closed':
114
+ // PR closed - update state
115
+ const newState = pull_request.merged ? 'merged' : 'closed';
116
+ config.db.updatePRState(repoId, prNumber, newState);
117
+ config.broadcaster?.broadcast('pr_closed', {
118
+ repo: repoFullName,
119
+ prNumber,
120
+ state: newState,
121
+ timestamp: new Date().toISOString(),
122
+ });
123
+ console.error(`✅ Updated PR ${repoFullName}#${prNumber} state to ${newState}`);
124
+ break;
125
+ case 'reopened':
126
+ // PR reopened - update state and re-score
127
+ config.db.updatePRState(repoId, prNumber, 'open');
128
+ await scorePR(repoFullName, prNumber, config);
129
+ console.error(`✅ Re-opened and re-scored PR ${repoFullName}#${prNumber}`);
130
+ break;
131
+ default:
132
+ console.error(`⏭️ Ignoring action: ${action}`);
133
+ }
134
+ }
135
+ catch (error) {
136
+ console.error(`❌ Failed to process PR ${repoFullName}#${prNumber}:`, error.message);
137
+ throw error;
138
+ }
139
+ }
140
+ /**
141
+ * Score a specific PR and save to database
142
+ */
143
+ async function scorePR(repoFullName, prNumber, config) {
144
+ const [owner, repo] = repoFullName.split('/');
145
+ // Create scanner with repository-specific config
146
+ const scanConfig = {
147
+ ...config.treliqConfig,
148
+ repo: repoFullName,
149
+ dbPath: config.db instanceof db_1.TreliqDB ? config.db.db.name : undefined,
150
+ };
151
+ const scanner = new scanner_1.TreliqScanner(scanConfig);
152
+ // Fetch and score the specific PR
153
+ const prs = await scanner.fetchPRDetails([prNumber]);
154
+ if (prs.length === 0) {
155
+ throw new Error(`PR #${prNumber} not found`);
156
+ }
157
+ const pr = prs[0];
158
+ // Score the PR
159
+ const scoredPR = await scanner.scoring.score(pr);
160
+ // Save to database
161
+ const repoId = config.db.upsertRepository(owner, repo);
162
+ const configHash = 'webhook'; // Use a special hash for webhook-triggered scores
163
+ config.db.upsertPR(repoId, scoredPR, configHash);
164
+ console.error(` Score: ${scoredPR.totalScore}/100 ${scoredPR.isSpam ? '(SPAM)' : ''}`);
165
+ // Broadcast to connected dashboard clients
166
+ config.broadcaster?.broadcast('pr_scored', {
167
+ repo: repoFullName,
168
+ prNumber,
169
+ title: pr.title,
170
+ totalScore: scoredPR.totalScore,
171
+ isSpam: scoredPR.isSpam,
172
+ timestamp: new Date().toISOString(),
173
+ });
174
+ }
175
+ //# sourceMappingURL=webhooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/server/webhooks.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAyDH,4CA8DC;AApHD,mCAAoC;AACpC,6CAAgD;AAChD,mCAAsC;AAiCtC;;GAEG;AACH,SAAS,eAAe,CAAC,OAAe,EAAE,SAAiB,EAAE,MAAc;IACzE,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAE7B,MAAM,IAAI,GAAG,IAAA,mBAAU,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7B,MAAM,MAAM,GAAG,UAAU,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAE9C,qDAAqD;IACrD,IAAI,SAAS,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAErD,OAAO,SAAS,KAAK,MAAM,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,SAAgB,gBAAgB,CAC9B,OAAwB,EACxB,MAAqB;IAErB,OAAO,CAAC,IAAI,CACV,WAAW,EACX;QACE,MAAM,EAAE;YACN,6CAA6C;YAC7C,OAAO,EAAE,IAAI;SACd;KACF,EACD,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QACrD,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAuB,CAAC;QAC/E,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAuB,CAAC;QACtE,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAuB,CAAC;QAE9E,OAAO,CAAC,KAAK,CAAC,wBAAwB,KAAK,eAAe,UAAU,GAAG,CAAC,CAAC;QAEzE,0CAA0C;QAC1C,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC1B,KAAK,EAAE,sBAAsB;aAC9B,CAAC,CAAC;QACL,CAAC;QAED,mBAAmB;QACnB,IAAI,CAAC,SAAS,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YACtE,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;YAC/C,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC1B,KAAK,EAAE,mBAAmB;aAC3B,CAAC,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,IAAsB,CAAC;QAE/C,kCAAkC;QAClC,IAAI,CAAC;YACH,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;gBAC7B,MAAM,sBAAsB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC9C,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;YACvD,CAAC;iBAAM,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;gBAC5B,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;gBAC1C,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;YAClD,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CAAC,uBAAuB,KAAK,EAAE,CAAC,CAAC;gBAC9C,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;YACpD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC1B,KAAK,EAAE,2BAA2B;gBAClC,OAAO,EAAE,KAAK,CAAC,OAAO;aACvB,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CACF,CAAC;IAEF,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;AACnE,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,sBAAsB,CACnC,OAAuB,EACvB,MAAqB;IAErB,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAErD,IAAI,CAAC,YAAY,IAAI,CAAC,UAAU,EAAE,CAAC;QACjC,OAAO,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACnE,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC;IACrC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC;IAC7B,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC;IACrC,MAAM,YAAY,GAAG,GAAG,KAAK,IAAI,IAAI,EAAE,CAAC;IAExC,OAAO,CAAC,KAAK,CAAC,iBAAiB,MAAM,QAAQ,YAAY,IAAI,QAAQ,EAAE,CAAC,CAAC;IAEzE,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAEvD,IAAI,CAAC;QACH,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,QAAQ;gBACX,2BAA2B;gBAC3B,MAAM,OAAO,CAAC,YAAY,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAC9C,OAAO,CAAC,KAAK,CAAC,mBAAmB,YAAY,IAAI,QAAQ,EAAE,CAAC,CAAC;gBAC7D,MAAM;YAER,KAAK,aAAa;gBAChB,sCAAsC;gBACtC,MAAM,OAAO,CAAC,YAAY,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAC9C,OAAO,CAAC,KAAK,CAAC,0BAA0B,YAAY,IAAI,QAAQ,EAAE,CAAC,CAAC;gBACpE,MAAM;YAER,KAAK,QAAQ;gBACX,2BAA2B;gBAC3B,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAC3D,MAAM,CAAC,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBACpD,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,WAAW,EAAE;oBACzC,IAAI,EAAE,YAAY;oBAClB,QAAQ;oBACR,KAAK,EAAE,QAAQ;oBACf,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBACpC,CAAC,CAAC;gBACH,OAAO,CAAC,KAAK,CAAC,gBAAgB,YAAY,IAAI,QAAQ,aAAa,QAAQ,EAAE,CAAC,CAAC;gBAC/E,MAAM;YAER,KAAK,UAAU;gBACb,0CAA0C;gBAC1C,MAAM,CAAC,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAClD,MAAM,OAAO,CAAC,YAAY,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAC9C,OAAO,CAAC,KAAK,CAAC,gCAAgC,YAAY,IAAI,QAAQ,EAAE,CAAC,CAAC;gBAC1E,MAAM;YAER;gBACE,OAAO,CAAC,KAAK,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,0BAA0B,YAAY,IAAI,QAAQ,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACpF,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,OAAO,CACpB,YAAoB,EACpB,QAAgB,EAChB,MAAqB;IAErB,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE9C,iDAAiD;IACjD,MAAM,UAAU,GAAiB;QAC/B,GAAG,MAAM,CAAC,YAAY;QACtB,IAAI,EAAE,YAAY;QAClB,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,aAAQ,CAAC,CAAC,CAAE,MAAM,CAAC,EAAU,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;KAC/E,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,uBAAa,CAAC,UAAU,CAAC,CAAC;IAE9C,kCAAkC;IAClC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAErD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,OAAO,QAAQ,YAAY,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IAElB,eAAe;IACf,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAEjD,mBAAmB;IACnB,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,kDAAkD;IAChF,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IAEjD,OAAO,CAAC,KAAK,CAAC,aAAa,QAAQ,CAAC,UAAU,QAAQ,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEzF,2CAA2C;IAC3C,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,WAAW,EAAE;QACzC,IAAI,EAAE,YAAY;QAClB,QAAQ;QACR,KAAK,EAAE,EAAE,CAAC,KAAK;QACf,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC,CAAC;AACL,CAAC"}