@juspay/yama 1.0.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.
- package/CHANGELOG.md +51 -0
- package/README.md +828 -0
- package/dist/cli/index.d.ts +12 -0
- package/dist/cli/index.js +541 -0
- package/dist/core/ContextGatherer.d.ts +105 -0
- package/dist/core/ContextGatherer.js +454 -0
- package/dist/core/Guardian.d.ts +80 -0
- package/dist/core/Guardian.js +457 -0
- package/dist/core/providers/BitbucketProvider.d.ts +105 -0
- package/dist/core/providers/BitbucketProvider.js +444 -0
- package/dist/features/CodeReviewer.d.ts +105 -0
- package/dist/features/CodeReviewer.js +1041 -0
- package/dist/features/DescriptionEnhancer.d.ts +64 -0
- package/dist/features/DescriptionEnhancer.js +448 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +60 -0
- package/dist/types/index.d.ts +419 -0
- package/dist/types/index.js +44 -0
- package/dist/utils/Cache.d.ts +92 -0
- package/dist/utils/Cache.js +255 -0
- package/dist/utils/ConfigManager.d.ts +84 -0
- package/dist/utils/ConfigManager.js +590 -0
- package/dist/utils/Logger.d.ts +30 -0
- package/dist/utils/Logger.js +217 -0
- package/package.json +138 -0
- package/yama.config.example.yaml +143 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Enhanced Cache utility for Yama
|
|
4
|
+
* Provides intelligent caching for PR data, file contents, and AI responses
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.cache = exports.Cache = void 0;
|
|
11
|
+
exports.createCache = createCache;
|
|
12
|
+
const node_cache_1 = __importDefault(require("node-cache"));
|
|
13
|
+
const Logger_1 = require("./Logger");
|
|
14
|
+
class Cache {
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.statsData = {
|
|
17
|
+
hits: 0,
|
|
18
|
+
misses: 0,
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Cache with tags for group invalidation
|
|
22
|
+
*/
|
|
23
|
+
this.tags = new Map();
|
|
24
|
+
const { ttl = 3600, // 1 hour default
|
|
25
|
+
maxSize = 100, // 100 keys max
|
|
26
|
+
checkPeriod = 600, // Check every 10 minutes
|
|
27
|
+
} = options;
|
|
28
|
+
this.cache = new node_cache_1.default({
|
|
29
|
+
stdTTL: ttl,
|
|
30
|
+
maxKeys: maxSize,
|
|
31
|
+
checkperiod: checkPeriod,
|
|
32
|
+
useClones: false,
|
|
33
|
+
deleteOnExpire: true,
|
|
34
|
+
});
|
|
35
|
+
this.cache.on("set", (key, _value) => {
|
|
36
|
+
Logger_1.logger.debug(`Cache SET: ${key}`);
|
|
37
|
+
});
|
|
38
|
+
this.cache.on("expired", (key, _value) => {
|
|
39
|
+
Logger_1.logger.debug(`Cache EXPIRED: ${key}`);
|
|
40
|
+
});
|
|
41
|
+
this.cache.on("del", (key, _value) => {
|
|
42
|
+
Logger_1.logger.debug(`Cache DELETE: ${key}`);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get value from cache
|
|
47
|
+
*/
|
|
48
|
+
get(key) {
|
|
49
|
+
const value = this.cache.get(key);
|
|
50
|
+
if (value !== undefined) {
|
|
51
|
+
this.statsData.hits++;
|
|
52
|
+
Logger_1.logger.debug(`Cache HIT: ${key}`);
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
this.statsData.misses++;
|
|
57
|
+
Logger_1.logger.debug(`Cache MISS: ${key}`);
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Set value in cache with optional TTL
|
|
63
|
+
*/
|
|
64
|
+
set(key, value, ttl) {
|
|
65
|
+
try {
|
|
66
|
+
const success = this.cache.set(key, value, ttl || 0);
|
|
67
|
+
if (success) {
|
|
68
|
+
Logger_1.logger.debug(`Cache SET successful: ${key}`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
Logger_1.logger.warn(`Cache SET failed: ${key}`);
|
|
72
|
+
}
|
|
73
|
+
return success;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
Logger_1.logger.error(`Cache SET error: ${key}`, error);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Delete key from cache
|
|
82
|
+
*/
|
|
83
|
+
del(key) {
|
|
84
|
+
const deleted = this.cache.del(key);
|
|
85
|
+
Logger_1.logger.debug(`Cache DELETE: ${key}, deleted: ${deleted}`);
|
|
86
|
+
return deleted;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Check if key exists in cache
|
|
90
|
+
*/
|
|
91
|
+
has(key) {
|
|
92
|
+
return this.cache.has(key);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Clear all cache entries
|
|
96
|
+
*/
|
|
97
|
+
clear() {
|
|
98
|
+
this.cache.flushAll();
|
|
99
|
+
this.statsData.hits = 0;
|
|
100
|
+
this.statsData.misses = 0;
|
|
101
|
+
Logger_1.logger.debug("Cache cleared");
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get all cache keys
|
|
105
|
+
*/
|
|
106
|
+
keys() {
|
|
107
|
+
return this.cache.keys();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get cache statistics
|
|
111
|
+
*/
|
|
112
|
+
stats() {
|
|
113
|
+
return {
|
|
114
|
+
hits: this.statsData.hits,
|
|
115
|
+
misses: this.statsData.misses,
|
|
116
|
+
keys: this.cache.keys().length,
|
|
117
|
+
size: this.cache.getStats().keys,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get detailed cache statistics from node-cache
|
|
122
|
+
*/
|
|
123
|
+
getDetailedStats() {
|
|
124
|
+
return this.cache.getStats();
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get or set pattern - common caching pattern
|
|
128
|
+
*/
|
|
129
|
+
async getOrSet(key, fetchFn, ttl) {
|
|
130
|
+
const cached = this.get(key);
|
|
131
|
+
if (cached !== undefined) {
|
|
132
|
+
return cached;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
Logger_1.logger.debug(`Cache FETCH: ${key}`);
|
|
136
|
+
const value = await fetchFn();
|
|
137
|
+
this.set(key, value, ttl);
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
Logger_1.logger.error(`Cache FETCH error: ${key}`, error);
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
setWithTags(key, value, tags, ttl) {
|
|
146
|
+
const success = this.set(key, value, ttl);
|
|
147
|
+
if (success) {
|
|
148
|
+
// Associate key with tags
|
|
149
|
+
tags.forEach((tag) => {
|
|
150
|
+
if (!this.tags.has(tag)) {
|
|
151
|
+
this.tags.set(tag, new Set());
|
|
152
|
+
}
|
|
153
|
+
this.tags.get(tag).add(key);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return success;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Invalidate all keys with a specific tag
|
|
160
|
+
*/
|
|
161
|
+
invalidateTag(tag) {
|
|
162
|
+
const keys = this.tags.get(tag);
|
|
163
|
+
if (!keys) {
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
let deleted = 0;
|
|
167
|
+
keys.forEach((key) => {
|
|
168
|
+
deleted += this.del(key);
|
|
169
|
+
});
|
|
170
|
+
// Clean up tag associations
|
|
171
|
+
this.tags.delete(tag);
|
|
172
|
+
Logger_1.logger.debug(`Invalidated tag "${tag}": ${deleted} keys`);
|
|
173
|
+
return deleted;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Smart cache warming for common patterns
|
|
177
|
+
*/
|
|
178
|
+
async warmPRCache(workspace, repository, prId) {
|
|
179
|
+
Logger_1.logger.debug(`Warming cache for PR ${workspace}/${repository}#${prId}`);
|
|
180
|
+
// Pre-generate cache keys that are likely to be needed
|
|
181
|
+
const keys = [
|
|
182
|
+
Cache.keys.prInfo(workspace, repository, prId),
|
|
183
|
+
Cache.keys.prDiff(workspace, repository, prId),
|
|
184
|
+
];
|
|
185
|
+
// This would be implemented by the calling code to actually fetch the data
|
|
186
|
+
Logger_1.logger.debug(`Cache warming prepared for keys: ${keys.join(", ")}`);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Cleanup expired entries and optimize memory
|
|
190
|
+
*/
|
|
191
|
+
cleanup() {
|
|
192
|
+
// Node-cache handles TTL cleanup automatically, but we can force it
|
|
193
|
+
const beforeKeys = this.cache.keys().length;
|
|
194
|
+
// Force check for expired keys
|
|
195
|
+
this.cache.keys().forEach((key) => {
|
|
196
|
+
this.cache.get(key); // This triggers expiry check
|
|
197
|
+
});
|
|
198
|
+
const afterKeys = this.cache.keys().length;
|
|
199
|
+
const cleaned = beforeKeys - afterKeys;
|
|
200
|
+
if (cleaned > 0) {
|
|
201
|
+
Logger_1.logger.debug(`Cache cleanup: removed ${cleaned} expired entries`);
|
|
202
|
+
}
|
|
203
|
+
// Clean up tag associations for deleted keys
|
|
204
|
+
this.tags.forEach((keys, tag) => {
|
|
205
|
+
const validKeys = new Set([...keys].filter((key) => this.cache.has(key)));
|
|
206
|
+
if (validKeys.size !== keys.size) {
|
|
207
|
+
this.tags.set(tag, validKeys);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get cache hit ratio
|
|
213
|
+
*/
|
|
214
|
+
getHitRatio() {
|
|
215
|
+
const total = this.statsData.hits + this.statsData.misses;
|
|
216
|
+
return total > 0 ? this.statsData.hits / total : 0;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Export cache state for debugging
|
|
220
|
+
*/
|
|
221
|
+
debug() {
|
|
222
|
+
return {
|
|
223
|
+
stats: this.stats(),
|
|
224
|
+
hitRatio: this.getHitRatio(),
|
|
225
|
+
detailedStats: this.getDetailedStats(),
|
|
226
|
+
keys: this.keys(),
|
|
227
|
+
tags: Object.fromEntries(this.tags.entries()),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
exports.Cache = Cache;
|
|
232
|
+
/**
|
|
233
|
+
* Cache key generators for common patterns
|
|
234
|
+
*/
|
|
235
|
+
Cache.keys = {
|
|
236
|
+
prInfo: (workspace, repository, prId) => `pr:${workspace}:${repository}:${prId}`,
|
|
237
|
+
prDiff: (workspace, repository, prId) => `diff:${workspace}:${repository}:${prId}`,
|
|
238
|
+
fileContent: (workspace, repository, filePath, branch) => `file:${workspace}:${repository}:${branch}:${filePath}`,
|
|
239
|
+
directoryContent: (workspace, repository, path, branch) => `dir:${workspace}:${repository}:${branch}:${path}`,
|
|
240
|
+
branchInfo: (workspace, repository, branch) => `branch:${workspace}:${repository}:${branch}`,
|
|
241
|
+
aiResponse: (prompt, provider, model) => {
|
|
242
|
+
// Create a hash of the prompt for consistent keys
|
|
243
|
+
const hash = Buffer.from(prompt).toString("base64").slice(0, 16);
|
|
244
|
+
return `ai:${provider}:${model}:${hash}`;
|
|
245
|
+
},
|
|
246
|
+
projectContext: (workspace, repository, branch) => `context:${workspace}:${repository}:${branch}`,
|
|
247
|
+
reviewResult: (workspace, repository, prId, configHash) => `review:${workspace}:${repository}:${prId}:${configHash}`,
|
|
248
|
+
};
|
|
249
|
+
// Export singleton instance
|
|
250
|
+
exports.cache = new Cache();
|
|
251
|
+
// Export factory function
|
|
252
|
+
function createCache(options) {
|
|
253
|
+
return new Cache(options);
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=Cache.js.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Configuration Manager for Yama
|
|
3
|
+
* Handles configuration loading, validation, and merging from multiple sources
|
|
4
|
+
*/
|
|
5
|
+
import { GuardianConfig } from "../types";
|
|
6
|
+
export declare class ConfigManager {
|
|
7
|
+
private config;
|
|
8
|
+
private configPaths;
|
|
9
|
+
/**
|
|
10
|
+
* Default configuration
|
|
11
|
+
*/
|
|
12
|
+
private static readonly DEFAULT_CONFIG;
|
|
13
|
+
constructor();
|
|
14
|
+
/**
|
|
15
|
+
* Setup configuration file search paths
|
|
16
|
+
*/
|
|
17
|
+
private setupConfigPaths;
|
|
18
|
+
/**
|
|
19
|
+
* Load configuration from files and environment
|
|
20
|
+
*/
|
|
21
|
+
loadConfig(configPath?: string): Promise<GuardianConfig>;
|
|
22
|
+
/**
|
|
23
|
+
* Load configuration from a specific file
|
|
24
|
+
*/
|
|
25
|
+
private loadConfigFile;
|
|
26
|
+
/**
|
|
27
|
+
* Apply environment variable overrides
|
|
28
|
+
*/
|
|
29
|
+
private applyEnvironmentOverrides;
|
|
30
|
+
/**
|
|
31
|
+
* Validate configuration
|
|
32
|
+
*/
|
|
33
|
+
private validateConfig;
|
|
34
|
+
/**
|
|
35
|
+
* Merge two configuration objects deeply
|
|
36
|
+
*/
|
|
37
|
+
private mergeConfigs;
|
|
38
|
+
/**
|
|
39
|
+
* Deep merge utility
|
|
40
|
+
*/
|
|
41
|
+
private deepMerge;
|
|
42
|
+
/**
|
|
43
|
+
* Deep clone utility
|
|
44
|
+
*/
|
|
45
|
+
private deepClone;
|
|
46
|
+
/**
|
|
47
|
+
* Get current configuration
|
|
48
|
+
*/
|
|
49
|
+
getConfig(): GuardianConfig;
|
|
50
|
+
/**
|
|
51
|
+
* Create default configuration file
|
|
52
|
+
*/
|
|
53
|
+
createDefaultConfig(outputPath?: string): Promise<string>;
|
|
54
|
+
/**
|
|
55
|
+
* Add helpful comments to configuration file
|
|
56
|
+
*/
|
|
57
|
+
private addConfigComments;
|
|
58
|
+
/**
|
|
59
|
+
* Validate specific configuration section
|
|
60
|
+
*/
|
|
61
|
+
validateSection(section: keyof GuardianConfig, config: any): boolean;
|
|
62
|
+
private validateProviders;
|
|
63
|
+
private validateFeatures;
|
|
64
|
+
private validateCache;
|
|
65
|
+
/**
|
|
66
|
+
* Get configuration schema for validation
|
|
67
|
+
*/
|
|
68
|
+
getSchema(): any;
|
|
69
|
+
/**
|
|
70
|
+
* Find the first available configuration file
|
|
71
|
+
*/
|
|
72
|
+
private findConfigFile;
|
|
73
|
+
/**
|
|
74
|
+
* Watch configuration file for changes and reload automatically
|
|
75
|
+
*/
|
|
76
|
+
watchConfig(callback?: (config: GuardianConfig) => void): () => void;
|
|
77
|
+
/**
|
|
78
|
+
* Enable hot-reload for configuration
|
|
79
|
+
*/
|
|
80
|
+
enableHotReload(callback?: (config: GuardianConfig) => void): () => void;
|
|
81
|
+
}
|
|
82
|
+
export declare const configManager: ConfigManager;
|
|
83
|
+
export declare function createConfigManager(): ConfigManager;
|
|
84
|
+
//# sourceMappingURL=ConfigManager.d.ts.map
|