@juspay/yama 1.6.0 → 2.1.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/.mcp-config.example.json +26 -0
- package/CHANGELOG.md +46 -0
- package/README.md +311 -685
- package/dist/cli/v2.cli.d.ts +13 -0
- package/dist/cli/v2.cli.js +359 -0
- package/dist/index.d.ts +12 -13
- package/dist/index.js +18 -19
- package/dist/v2/config/ConfigLoader.d.ts +50 -0
- package/dist/v2/config/ConfigLoader.js +205 -0
- package/dist/v2/config/DefaultConfig.d.ts +9 -0
- package/dist/v2/config/DefaultConfig.js +187 -0
- package/dist/v2/core/LearningOrchestrator.d.ts +65 -0
- package/dist/v2/core/LearningOrchestrator.js +499 -0
- package/dist/v2/core/MCPServerManager.d.ts +22 -0
- package/dist/v2/core/MCPServerManager.js +100 -0
- package/dist/v2/core/SessionManager.d.ts +72 -0
- package/dist/v2/core/SessionManager.js +200 -0
- package/dist/v2/core/YamaV2Orchestrator.d.ts +112 -0
- package/dist/v2/core/YamaV2Orchestrator.js +549 -0
- package/dist/v2/learning/FeedbackExtractor.d.ts +46 -0
- package/dist/v2/learning/FeedbackExtractor.js +237 -0
- package/dist/v2/learning/KnowledgeBaseManager.d.ts +91 -0
- package/dist/v2/learning/KnowledgeBaseManager.js +475 -0
- package/dist/v2/learning/types.d.ts +121 -0
- package/dist/v2/learning/types.js +15 -0
- package/dist/v2/prompts/EnhancementSystemPrompt.d.ts +8 -0
- package/dist/v2/prompts/EnhancementSystemPrompt.js +216 -0
- package/dist/v2/prompts/LangfusePromptManager.d.ts +48 -0
- package/dist/v2/prompts/LangfusePromptManager.js +144 -0
- package/dist/v2/prompts/LearningSystemPrompt.d.ts +11 -0
- package/dist/v2/prompts/LearningSystemPrompt.js +180 -0
- package/dist/v2/prompts/PromptBuilder.d.ts +45 -0
- package/dist/v2/prompts/PromptBuilder.js +257 -0
- package/dist/v2/prompts/ReviewSystemPrompt.d.ts +8 -0
- package/dist/v2/prompts/ReviewSystemPrompt.js +270 -0
- package/dist/v2/types/config.types.d.ts +141 -0
- package/dist/v2/types/config.types.js +5 -0
- package/dist/v2/types/mcp.types.d.ts +191 -0
- package/dist/v2/types/mcp.types.js +6 -0
- package/dist/v2/types/v2.types.d.ts +182 -0
- package/dist/v2/types/v2.types.js +42 -0
- package/dist/v2/utils/ObservabilityConfig.d.ts +22 -0
- package/dist/v2/utils/ObservabilityConfig.js +48 -0
- package/package.json +16 -10
- package/yama.config.example.yaml +259 -204
- package/dist/cli/index.d.ts +0 -12
- package/dist/cli/index.js +0 -538
- package/dist/core/ContextGatherer.d.ts +0 -110
- package/dist/core/ContextGatherer.js +0 -470
- package/dist/core/Guardian.d.ts +0 -81
- package/dist/core/Guardian.js +0 -480
- package/dist/core/providers/BitbucketProvider.d.ts +0 -105
- package/dist/core/providers/BitbucketProvider.js +0 -489
- package/dist/features/CodeReviewer.d.ts +0 -173
- package/dist/features/CodeReviewer.js +0 -1707
- package/dist/features/DescriptionEnhancer.d.ts +0 -70
- package/dist/features/DescriptionEnhancer.js +0 -511
- package/dist/features/MultiInstanceProcessor.d.ts +0 -74
- package/dist/features/MultiInstanceProcessor.js +0 -360
- package/dist/types/index.d.ts +0 -624
- package/dist/types/index.js +0 -104
- package/dist/utils/Cache.d.ts +0 -103
- package/dist/utils/Cache.js +0 -444
- package/dist/utils/ConfigManager.d.ts +0 -88
- package/dist/utils/ConfigManager.js +0 -602
- package/dist/utils/ContentSimilarityService.d.ts +0 -74
- package/dist/utils/ContentSimilarityService.js +0 -215
- package/dist/utils/ExactDuplicateRemover.d.ts +0 -77
- package/dist/utils/ExactDuplicateRemover.js +0 -361
- package/dist/utils/Logger.d.ts +0 -31
- package/dist/utils/Logger.js +0 -214
- package/dist/utils/MemoryBankManager.d.ts +0 -73
- package/dist/utils/MemoryBankManager.js +0 -310
- package/dist/utils/ParallelProcessing.d.ts +0 -140
- package/dist/utils/ParallelProcessing.js +0 -333
- package/dist/utils/ProviderLimits.d.ts +0 -58
- package/dist/utils/ProviderLimits.js +0 -143
- package/dist/utils/RetryManager.d.ts +0 -78
- package/dist/utils/RetryManager.js +0 -205
package/dist/types/index.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core TypeScript types for Yama
|
|
3
|
-
* Consolidates all interfaces and types used across the application
|
|
4
|
-
*/
|
|
5
|
-
// ============================================================================
|
|
6
|
-
// Error Types
|
|
7
|
-
// ============================================================================
|
|
8
|
-
export class GuardianError extends Error {
|
|
9
|
-
code;
|
|
10
|
-
context;
|
|
11
|
-
constructor(code, message, context) {
|
|
12
|
-
super(message);
|
|
13
|
-
this.code = code;
|
|
14
|
-
this.context = context;
|
|
15
|
-
this.name = "GuardianError";
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
export class ConfigurationError extends GuardianError {
|
|
19
|
-
constructor(message, context) {
|
|
20
|
-
super("CONFIGURATION_ERROR", message, context);
|
|
21
|
-
this.name = "ConfigurationError";
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
export class ProviderError extends GuardianError {
|
|
25
|
-
constructor(message, context) {
|
|
26
|
-
super("PROVIDER_ERROR", message, context);
|
|
27
|
-
this.name = "ProviderError";
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
export class ValidationError extends GuardianError {
|
|
31
|
-
constructor(message, context) {
|
|
32
|
-
super("VALIDATION_ERROR", message, context);
|
|
33
|
-
this.name = "ValidationError";
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
// ============================================================================
|
|
37
|
-
// Cache Error Types
|
|
38
|
-
// ============================================================================
|
|
39
|
-
export var CacheErrorCode;
|
|
40
|
-
(function (CacheErrorCode) {
|
|
41
|
-
// System-level cache errors
|
|
42
|
-
CacheErrorCode["CACHE_SYSTEM_FAILURE"] = "CACHE_SYSTEM_FAILURE";
|
|
43
|
-
CacheErrorCode["CACHE_MEMORY_EXHAUSTED"] = "CACHE_MEMORY_EXHAUSTED";
|
|
44
|
-
CacheErrorCode["CACHE_INITIALIZATION_FAILED"] = "CACHE_INITIALIZATION_FAILED";
|
|
45
|
-
// Storage-related errors
|
|
46
|
-
CacheErrorCode["CACHE_STORAGE_FULL"] = "CACHE_STORAGE_FULL";
|
|
47
|
-
CacheErrorCode["CACHE_STORAGE_PERMISSION"] = "CACHE_STORAGE_PERMISSION";
|
|
48
|
-
CacheErrorCode["CACHE_STORAGE_CORRUPTION"] = "CACHE_STORAGE_CORRUPTION";
|
|
49
|
-
// Network-related errors (for future Redis support)
|
|
50
|
-
CacheErrorCode["CACHE_NETWORK_CONNECTION"] = "CACHE_NETWORK_CONNECTION";
|
|
51
|
-
CacheErrorCode["CACHE_NETWORK_TIMEOUT"] = "CACHE_NETWORK_TIMEOUT";
|
|
52
|
-
CacheErrorCode["CACHE_NETWORK_AUTH"] = "CACHE_NETWORK_AUTH";
|
|
53
|
-
// Configuration errors
|
|
54
|
-
CacheErrorCode["CACHE_CONFIG_INVALID"] = "CACHE_CONFIG_INVALID";
|
|
55
|
-
CacheErrorCode["CACHE_CONFIG_MISSING"] = "CACHE_CONFIG_MISSING";
|
|
56
|
-
// Operation errors
|
|
57
|
-
CacheErrorCode["CACHE_OPERATION_FAILED"] = "CACHE_OPERATION_FAILED";
|
|
58
|
-
CacheErrorCode["CACHE_SERIALIZATION_ERROR"] = "CACHE_SERIALIZATION_ERROR";
|
|
59
|
-
CacheErrorCode["CACHE_KEY_INVALID"] = "CACHE_KEY_INVALID";
|
|
60
|
-
})(CacheErrorCode || (CacheErrorCode = {}));
|
|
61
|
-
export class CacheError extends GuardianError {
|
|
62
|
-
operation;
|
|
63
|
-
key;
|
|
64
|
-
constructor(code, message, operation, key, context) {
|
|
65
|
-
super(code, message, context);
|
|
66
|
-
this.operation = operation;
|
|
67
|
-
this.key = key;
|
|
68
|
-
this.name = "CacheError";
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
export class CacheSystemError extends CacheError {
|
|
72
|
-
constructor(message, operation, key, context) {
|
|
73
|
-
super(CacheErrorCode.CACHE_SYSTEM_FAILURE, message, operation, key, context);
|
|
74
|
-
this.name = "CacheSystemError";
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
export class CacheStorageError extends CacheError {
|
|
78
|
-
constructor(code = CacheErrorCode.CACHE_STORAGE_FULL, message, operation, key, context) {
|
|
79
|
-
super(code, message, operation, key, context);
|
|
80
|
-
this.name = "CacheStorageError";
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
export class CacheNetworkError extends CacheError {
|
|
84
|
-
constructor(code = CacheErrorCode.CACHE_NETWORK_CONNECTION, message, operation, key, context) {
|
|
85
|
-
super(code, message, operation, key, context);
|
|
86
|
-
this.name = "CacheNetworkError";
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
export class CacheConfigurationError extends CacheError {
|
|
90
|
-
constructor(code = CacheErrorCode.CACHE_CONFIG_INVALID, message, operation, key, context) {
|
|
91
|
-
super(code, message, operation, key, context);
|
|
92
|
-
this.name = "CacheConfigurationError";
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
export class CacheOperationError extends CacheError {
|
|
96
|
-
constructor(code = CacheErrorCode.CACHE_OPERATION_FAILED, message, operation, key, context) {
|
|
97
|
-
super(code, message, operation, key, context);
|
|
98
|
-
this.name = "CacheOperationError";
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// ============================================================================
|
|
102
|
-
// Export all types - Main file, no re-exports needed
|
|
103
|
-
// ============================================================================
|
|
104
|
-
//# sourceMappingURL=index.js.map
|
package/dist/utils/Cache.d.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enhanced Cache utility for Yama
|
|
3
|
-
* Provides intelligent caching for PR data, file contents, and AI responses
|
|
4
|
-
*/
|
|
5
|
-
import { Cache as ICache, CacheOptions } from "../types/index.js";
|
|
6
|
-
export declare class Cache implements ICache {
|
|
7
|
-
private cache;
|
|
8
|
-
private statsData;
|
|
9
|
-
constructor(options?: CacheOptions);
|
|
10
|
-
/**
|
|
11
|
-
* Get value from cache with resilient error handling
|
|
12
|
-
*/
|
|
13
|
-
get<T>(key: string): T | undefined;
|
|
14
|
-
/**
|
|
15
|
-
* Set value in cache with optional TTL
|
|
16
|
-
*/
|
|
17
|
-
set<T>(key: string, value: T, ttl?: number): boolean;
|
|
18
|
-
/**
|
|
19
|
-
* Delete key from cache
|
|
20
|
-
*/
|
|
21
|
-
del(key: string): number;
|
|
22
|
-
/**
|
|
23
|
-
* Check if key exists in cache
|
|
24
|
-
*/
|
|
25
|
-
has(key: string): boolean;
|
|
26
|
-
/**
|
|
27
|
-
* Clear all cache entries
|
|
28
|
-
*/
|
|
29
|
-
clear(): void;
|
|
30
|
-
/**
|
|
31
|
-
* Get all cache keys
|
|
32
|
-
*/
|
|
33
|
-
keys(): string[];
|
|
34
|
-
/**
|
|
35
|
-
* Get cache statistics
|
|
36
|
-
*/
|
|
37
|
-
stats(): {
|
|
38
|
-
hits: number;
|
|
39
|
-
misses: number;
|
|
40
|
-
keys: number;
|
|
41
|
-
size: number;
|
|
42
|
-
cacheErrors: number;
|
|
43
|
-
nonCacheErrors: number;
|
|
44
|
-
};
|
|
45
|
-
/**
|
|
46
|
-
* Get detailed cache statistics from node-cache
|
|
47
|
-
*/
|
|
48
|
-
getDetailedStats(): any;
|
|
49
|
-
/**
|
|
50
|
-
* Get or set pattern with automatic fallback on cache failures
|
|
51
|
-
*/
|
|
52
|
-
getOrSet<T>(key: string, fetchFn: () => Promise<T>, ttl?: number): Promise<T>;
|
|
53
|
-
/**
|
|
54
|
-
* Resilient get or set pattern that bypasses cache entirely on cache system failures
|
|
55
|
-
*/
|
|
56
|
-
getOrSetResilient<T>(key: string, fetchFn: () => Promise<T>, ttl?: number): Promise<T>;
|
|
57
|
-
/**
|
|
58
|
-
* Cache with tags for group invalidation
|
|
59
|
-
*/
|
|
60
|
-
private tags;
|
|
61
|
-
setWithTags<T>(key: string, value: T, tags: string[], ttl?: number): boolean;
|
|
62
|
-
/**
|
|
63
|
-
* Invalidate all keys with a specific tag
|
|
64
|
-
*/
|
|
65
|
-
invalidateTag(tag: string): number;
|
|
66
|
-
/**
|
|
67
|
-
* Invalidate all keys matching a pattern
|
|
68
|
-
*/
|
|
69
|
-
invalidatePattern(pattern: string): number;
|
|
70
|
-
/**
|
|
71
|
-
* Cache key generators for common patterns
|
|
72
|
-
*/
|
|
73
|
-
static keys: {
|
|
74
|
-
prInfo: (workspace: string, repository: string, prId: string | number) => string;
|
|
75
|
-
prDiff: (workspace: string, repository: string, prId: string | number) => string;
|
|
76
|
-
fileContent: (workspace: string, repository: string, filePath: string, branch: string) => string;
|
|
77
|
-
directoryContent: (workspace: string, repository: string, path: string, branch: string) => string;
|
|
78
|
-
branchInfo: (workspace: string, repository: string, branch: string) => string;
|
|
79
|
-
aiResponse: (prompt: string, provider: string, model: string) => string;
|
|
80
|
-
projectContext: (workspace: string, repository: string, branch: string) => string;
|
|
81
|
-
reviewResult: (workspace: string, repository: string, prId: string | number, configHash: string) => string;
|
|
82
|
-
memoryBankFiles: (workspace: string, repository: string, branch: string, path: string) => string;
|
|
83
|
-
};
|
|
84
|
-
/**
|
|
85
|
-
* Smart cache warming for common patterns
|
|
86
|
-
*/
|
|
87
|
-
warmPRCache(workspace: string, repository: string, prId: string | number): Promise<void>;
|
|
88
|
-
/**
|
|
89
|
-
* Cleanup expired entries and optimize memory
|
|
90
|
-
*/
|
|
91
|
-
cleanup(): void;
|
|
92
|
-
/**
|
|
93
|
-
* Get cache hit ratio
|
|
94
|
-
*/
|
|
95
|
-
getHitRatio(): number;
|
|
96
|
-
/**
|
|
97
|
-
* Export cache state for debugging
|
|
98
|
-
*/
|
|
99
|
-
debug(): any;
|
|
100
|
-
}
|
|
101
|
-
export declare const cache: Cache;
|
|
102
|
-
export declare function createCache(options?: CacheOptions): Cache;
|
|
103
|
-
//# sourceMappingURL=Cache.d.ts.map
|
package/dist/utils/Cache.js
DELETED
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enhanced Cache utility for Yama
|
|
3
|
-
* Provides intelligent caching for PR data, file contents, and AI responses
|
|
4
|
-
*/
|
|
5
|
-
import NodeCache from "node-cache";
|
|
6
|
-
import { CacheError, } from "../types/index.js";
|
|
7
|
-
import { logger } from "./Logger.js";
|
|
8
|
-
/**
|
|
9
|
-
* Enhanced cache error detection utility
|
|
10
|
-
* Provides multi-layer error classification to avoid false positives
|
|
11
|
-
*/
|
|
12
|
-
class CacheErrorDetector {
|
|
13
|
-
/**
|
|
14
|
-
* Detect if an error is cache-related using multiple strategies
|
|
15
|
-
*/
|
|
16
|
-
static isCacheError(error, operation, key) {
|
|
17
|
-
// Strategy 1: Check error type/class (most reliable)
|
|
18
|
-
if (error instanceof CacheError) {
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
// Strategy 2: Check for specific cache error patterns in NodeCache
|
|
22
|
-
if (error instanceof Error) {
|
|
23
|
-
const errorMessage = error.message.toLowerCase();
|
|
24
|
-
const stackTrace = error.stack?.toLowerCase() || "";
|
|
25
|
-
// Check for NodeCache-specific error patterns
|
|
26
|
-
const nodeCachePatterns = [
|
|
27
|
-
/node_modules\/node-cache/,
|
|
28
|
-
/cache\.js:\d+/,
|
|
29
|
-
/nodecache/,
|
|
30
|
-
];
|
|
31
|
-
const isNodeCacheError = nodeCachePatterns.some((pattern) => pattern.test(stackTrace));
|
|
32
|
-
if (isNodeCacheError) {
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
// Strategy 3: Check for specific cache-related error messages (more targeted)
|
|
36
|
-
const cacheSpecificPatterns = [
|
|
37
|
-
/cache.*(?:full|exhausted|limit)/,
|
|
38
|
-
/memory.*(?:cache|allocation).*(?:failed|error)/,
|
|
39
|
-
/storage.*(?:cache|quota).*(?:exceeded|full)/,
|
|
40
|
-
/cache.*(?:initialization|setup).*(?:failed|error)/,
|
|
41
|
-
/ttl.*(?:invalid|expired)/,
|
|
42
|
-
/cache.*(?:key|value).*(?:invalid|malformed)/,
|
|
43
|
-
];
|
|
44
|
-
const hasCacheSpecificError = cacheSpecificPatterns.some((pattern) => pattern.test(errorMessage));
|
|
45
|
-
if (hasCacheSpecificError) {
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
// Strategy 4: Context-aware detection
|
|
49
|
-
if (operation && key) {
|
|
50
|
-
// If we're in a cache operation and get memory/storage errors, likely cache-related
|
|
51
|
-
const cacheOperations = [
|
|
52
|
-
"get",
|
|
53
|
-
"set",
|
|
54
|
-
"del",
|
|
55
|
-
"clear",
|
|
56
|
-
"has",
|
|
57
|
-
"getorset",
|
|
58
|
-
"getorsetresilient",
|
|
59
|
-
];
|
|
60
|
-
const isCacheOperation = cacheOperations.includes(operation.toLowerCase());
|
|
61
|
-
const contextualPatterns = [
|
|
62
|
-
/^out of memory$/,
|
|
63
|
-
/storage quota exceeded/,
|
|
64
|
-
/disk full/,
|
|
65
|
-
];
|
|
66
|
-
if (isCacheOperation &&
|
|
67
|
-
contextualPatterns.some((pattern) => pattern.test(errorMessage))) {
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Classify cache error for better handling and logging
|
|
76
|
-
*/
|
|
77
|
-
static classifyError(error, operation, key) {
|
|
78
|
-
if (!this.isCacheError(error, operation, key)) {
|
|
79
|
-
return {
|
|
80
|
-
isCache: false,
|
|
81
|
-
category: "unknown",
|
|
82
|
-
confidence: "high",
|
|
83
|
-
reason: "Not identified as cache-related error",
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
if (error instanceof CacheError) {
|
|
87
|
-
const category = error.code.includes("STORAGE")
|
|
88
|
-
? "storage"
|
|
89
|
-
: error.code.includes("NETWORK")
|
|
90
|
-
? "network"
|
|
91
|
-
: error.code.includes("SYSTEM")
|
|
92
|
-
? "system"
|
|
93
|
-
: "operation";
|
|
94
|
-
return {
|
|
95
|
-
isCache: true,
|
|
96
|
-
category,
|
|
97
|
-
confidence: "high",
|
|
98
|
-
reason: `Explicit cache error: ${error.code}`,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
if (error instanceof Error) {
|
|
102
|
-
const message = error.message.toLowerCase();
|
|
103
|
-
const stack = error.stack?.toLowerCase() || "";
|
|
104
|
-
// High confidence patterns
|
|
105
|
-
if (/node_modules\/node-cache/.test(stack)) {
|
|
106
|
-
return {
|
|
107
|
-
isCache: true,
|
|
108
|
-
category: "system",
|
|
109
|
-
confidence: "high",
|
|
110
|
-
reason: "NodeCache stack trace detected",
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
// Medium confidence patterns
|
|
114
|
-
if (/cache.*(?:full|exhausted)/.test(message)) {
|
|
115
|
-
return {
|
|
116
|
-
isCache: true,
|
|
117
|
-
category: "storage",
|
|
118
|
-
confidence: "medium",
|
|
119
|
-
reason: "Cache capacity error pattern",
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
if (/memory.*cache.*failed/.test(message)) {
|
|
123
|
-
return {
|
|
124
|
-
isCache: true,
|
|
125
|
-
category: "system",
|
|
126
|
-
confidence: "medium",
|
|
127
|
-
reason: "Memory allocation error in cache context",
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return {
|
|
132
|
-
isCache: true,
|
|
133
|
-
category: "unknown",
|
|
134
|
-
confidence: "low",
|
|
135
|
-
reason: "Fallback detection",
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
export class Cache {
|
|
140
|
-
cache;
|
|
141
|
-
statsData = {
|
|
142
|
-
hits: 0,
|
|
143
|
-
misses: 0,
|
|
144
|
-
cacheErrors: 0,
|
|
145
|
-
nonCacheErrors: 0,
|
|
146
|
-
};
|
|
147
|
-
constructor(options = {}) {
|
|
148
|
-
const { ttl = 3600, // 1 hour default
|
|
149
|
-
maxSize = 100, // 100 keys max
|
|
150
|
-
checkPeriod = 600, // Check every 10 minutes
|
|
151
|
-
} = options;
|
|
152
|
-
this.cache = new NodeCache({
|
|
153
|
-
stdTTL: ttl,
|
|
154
|
-
maxKeys: maxSize,
|
|
155
|
-
checkperiod: checkPeriod,
|
|
156
|
-
useClones: false,
|
|
157
|
-
deleteOnExpire: true,
|
|
158
|
-
});
|
|
159
|
-
this.cache.on("set", (key, _value) => {
|
|
160
|
-
logger.debug(`Cache SET: ${key}`);
|
|
161
|
-
});
|
|
162
|
-
this.cache.on("expired", (key, _value) => {
|
|
163
|
-
logger.debug(`Cache EXPIRED: ${key}`);
|
|
164
|
-
});
|
|
165
|
-
this.cache.on("del", (key, _value) => {
|
|
166
|
-
logger.debug(`Cache DELETE: ${key}`);
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Get value from cache with resilient error handling
|
|
171
|
-
*/
|
|
172
|
-
get(key) {
|
|
173
|
-
try {
|
|
174
|
-
const value = this.cache.get(key);
|
|
175
|
-
if (value !== undefined) {
|
|
176
|
-
this.statsData.hits++;
|
|
177
|
-
logger.debug(`Cache HIT: ${key}`);
|
|
178
|
-
return value;
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
this.statsData.misses++;
|
|
182
|
-
logger.debug(`Cache MISS: ${key}`);
|
|
183
|
-
return undefined;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
this.statsData.misses++;
|
|
188
|
-
logger.warn(`Cache GET error for ${key}, treating as miss:`, error);
|
|
189
|
-
return undefined;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Set value in cache with optional TTL
|
|
194
|
-
*/
|
|
195
|
-
set(key, value, ttl) {
|
|
196
|
-
try {
|
|
197
|
-
const success = this.cache.set(key, value, ttl || 0);
|
|
198
|
-
if (success) {
|
|
199
|
-
logger.debug(`Cache SET successful: ${key}`);
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
logger.warn(`Cache SET failed: ${key}`);
|
|
203
|
-
}
|
|
204
|
-
return success;
|
|
205
|
-
}
|
|
206
|
-
catch (error) {
|
|
207
|
-
logger.error(`Cache SET error: ${key}`, error);
|
|
208
|
-
return false;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Delete key from cache
|
|
213
|
-
*/
|
|
214
|
-
del(key) {
|
|
215
|
-
const deleted = this.cache.del(key);
|
|
216
|
-
logger.debug(`Cache DELETE: ${key}, deleted: ${deleted}`);
|
|
217
|
-
return deleted;
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Check if key exists in cache
|
|
221
|
-
*/
|
|
222
|
-
has(key) {
|
|
223
|
-
return this.cache.has(key);
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Clear all cache entries
|
|
227
|
-
*/
|
|
228
|
-
clear() {
|
|
229
|
-
this.cache.flushAll();
|
|
230
|
-
this.statsData.hits = 0;
|
|
231
|
-
this.statsData.misses = 0;
|
|
232
|
-
logger.debug("Cache cleared");
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Get all cache keys
|
|
236
|
-
*/
|
|
237
|
-
keys() {
|
|
238
|
-
return this.cache.keys();
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Get cache statistics
|
|
242
|
-
*/
|
|
243
|
-
stats() {
|
|
244
|
-
return {
|
|
245
|
-
hits: this.statsData.hits,
|
|
246
|
-
misses: this.statsData.misses,
|
|
247
|
-
keys: this.cache.keys().length,
|
|
248
|
-
size: this.cache.getStats().keys,
|
|
249
|
-
cacheErrors: this.statsData.cacheErrors,
|
|
250
|
-
nonCacheErrors: this.statsData.nonCacheErrors,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Get detailed cache statistics from node-cache
|
|
255
|
-
*/
|
|
256
|
-
getDetailedStats() {
|
|
257
|
-
return this.cache.getStats();
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Get or set pattern with automatic fallback on cache failures
|
|
261
|
-
*/
|
|
262
|
-
async getOrSet(key, fetchFn, ttl) {
|
|
263
|
-
// Try to get from cache with resilient error handling
|
|
264
|
-
const cached = this.get(key);
|
|
265
|
-
if (cached !== undefined) {
|
|
266
|
-
return cached;
|
|
267
|
-
}
|
|
268
|
-
try {
|
|
269
|
-
logger.debug(`Cache FETCH: ${key}`);
|
|
270
|
-
const value = await fetchFn();
|
|
271
|
-
// Try to cache the result, but don't fail if caching fails
|
|
272
|
-
try {
|
|
273
|
-
this.set(key, value, ttl);
|
|
274
|
-
}
|
|
275
|
-
catch (cacheError) {
|
|
276
|
-
logger.warn(`Cache SET failed for ${key}, continuing without cache:`, cacheError);
|
|
277
|
-
}
|
|
278
|
-
return value;
|
|
279
|
-
}
|
|
280
|
-
catch (error) {
|
|
281
|
-
logger.error(`Cache FETCH error: ${key}`, error);
|
|
282
|
-
throw error;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Resilient get or set pattern that bypasses cache entirely on cache system failures
|
|
287
|
-
*/
|
|
288
|
-
async getOrSetResilient(key, fetchFn, ttl) {
|
|
289
|
-
try {
|
|
290
|
-
// Try normal cache flow first
|
|
291
|
-
return await this.getOrSet(key, fetchFn, ttl);
|
|
292
|
-
}
|
|
293
|
-
catch (error) {
|
|
294
|
-
// Use enhanced error detection to determine if this is a cache-related error
|
|
295
|
-
const errorClassification = CacheErrorDetector.classifyError(error, "getOrSet", key);
|
|
296
|
-
if (errorClassification.isCache) {
|
|
297
|
-
// Track cache error statistics
|
|
298
|
-
this.statsData.cacheErrors++;
|
|
299
|
-
logger.warn(`Cache system error detected for ${key} (${errorClassification.confidence} confidence: ${errorClassification.reason}), bypassing cache entirely`, {
|
|
300
|
-
error: error instanceof Error ? error.message : String(error),
|
|
301
|
-
category: errorClassification.category,
|
|
302
|
-
confidence: errorClassification.confidence,
|
|
303
|
-
key,
|
|
304
|
-
operation: "getOrSet",
|
|
305
|
-
});
|
|
306
|
-
// Bypass cache completely and just fetch the data
|
|
307
|
-
return await fetchFn();
|
|
308
|
-
}
|
|
309
|
-
// Track non-cache errors for debugging
|
|
310
|
-
this.statsData.nonCacheErrors++;
|
|
311
|
-
// Re-throw non-cache errors
|
|
312
|
-
throw error;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
/**
|
|
316
|
-
* Cache with tags for group invalidation
|
|
317
|
-
*/
|
|
318
|
-
tags = new Map();
|
|
319
|
-
setWithTags(key, value, tags, ttl) {
|
|
320
|
-
const success = this.set(key, value, ttl);
|
|
321
|
-
if (success) {
|
|
322
|
-
// Associate key with tags
|
|
323
|
-
tags.forEach((tag) => {
|
|
324
|
-
if (!this.tags.has(tag)) {
|
|
325
|
-
this.tags.set(tag, new Set());
|
|
326
|
-
}
|
|
327
|
-
this.tags.get(tag).add(key);
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
return success;
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Invalidate all keys with a specific tag
|
|
334
|
-
*/
|
|
335
|
-
invalidateTag(tag) {
|
|
336
|
-
const keys = this.tags.get(tag);
|
|
337
|
-
if (!keys) {
|
|
338
|
-
return 0;
|
|
339
|
-
}
|
|
340
|
-
let deleted = 0;
|
|
341
|
-
keys.forEach((key) => {
|
|
342
|
-
deleted += this.del(key);
|
|
343
|
-
});
|
|
344
|
-
// Clean up tag associations
|
|
345
|
-
this.tags.delete(tag);
|
|
346
|
-
logger.debug(`Invalidated tag "${tag}": ${deleted} keys`);
|
|
347
|
-
return deleted;
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* Invalidate all keys matching a pattern
|
|
351
|
-
*/
|
|
352
|
-
invalidatePattern(pattern) {
|
|
353
|
-
const regex = new RegExp(pattern.replace(/\*/g, ".*"));
|
|
354
|
-
const allKeys = this.keys();
|
|
355
|
-
let deleted = 0;
|
|
356
|
-
allKeys.forEach((key) => {
|
|
357
|
-
if (regex.test(key)) {
|
|
358
|
-
deleted += this.del(key);
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
logger.debug(`Invalidated pattern "${pattern}": ${deleted} keys`);
|
|
362
|
-
return deleted;
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Cache key generators for common patterns
|
|
366
|
-
*/
|
|
367
|
-
static keys = {
|
|
368
|
-
prInfo: (workspace, repository, prId) => `pr:${workspace}:${repository}:${prId}`,
|
|
369
|
-
prDiff: (workspace, repository, prId) => `diff:${workspace}:${repository}:${prId}`,
|
|
370
|
-
fileContent: (workspace, repository, filePath, branch) => `file:${workspace}:${repository}:${branch}:${filePath}`,
|
|
371
|
-
directoryContent: (workspace, repository, path, branch) => `dir:${workspace}:${repository}:${branch}:${path}`,
|
|
372
|
-
branchInfo: (workspace, repository, branch) => `branch:${workspace}:${repository}:${branch}`,
|
|
373
|
-
aiResponse: (prompt, provider, model) => {
|
|
374
|
-
// Create a hash of the prompt for consistent keys
|
|
375
|
-
const hash = Buffer.from(prompt).toString("base64").slice(0, 16);
|
|
376
|
-
return `ai:${provider}:${model}:${hash}`;
|
|
377
|
-
},
|
|
378
|
-
projectContext: (workspace, repository, branch) => `context:${workspace}:${repository}:${branch}`,
|
|
379
|
-
reviewResult: (workspace, repository, prId, configHash) => `review:${workspace}:${repository}:${prId}:${configHash}`,
|
|
380
|
-
memoryBankFiles: (workspace, repository, branch, path) => `memory-bank:${workspace}:${repository}:${branch}:${path}`,
|
|
381
|
-
};
|
|
382
|
-
/**
|
|
383
|
-
* Smart cache warming for common patterns
|
|
384
|
-
*/
|
|
385
|
-
async warmPRCache(workspace, repository, prId) {
|
|
386
|
-
logger.debug(`Warming cache for PR ${workspace}/${repository}#${prId}`);
|
|
387
|
-
// Pre-generate cache keys that are likely to be needed
|
|
388
|
-
const keys = [
|
|
389
|
-
Cache.keys.prInfo(workspace, repository, prId),
|
|
390
|
-
Cache.keys.prDiff(workspace, repository, prId),
|
|
391
|
-
];
|
|
392
|
-
// This would be implemented by the calling code to actually fetch the data
|
|
393
|
-
logger.debug(`Cache warming prepared for keys: ${keys.join(", ")}`);
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* Cleanup expired entries and optimize memory
|
|
397
|
-
*/
|
|
398
|
-
cleanup() {
|
|
399
|
-
// Node-cache handles TTL cleanup automatically, but we can force it
|
|
400
|
-
const beforeKeys = this.cache.keys().length;
|
|
401
|
-
// Force check for expired keys
|
|
402
|
-
this.cache.keys().forEach((key) => {
|
|
403
|
-
this.cache.get(key); // This triggers expiry check
|
|
404
|
-
});
|
|
405
|
-
const afterKeys = this.cache.keys().length;
|
|
406
|
-
const cleaned = beforeKeys - afterKeys;
|
|
407
|
-
if (cleaned > 0) {
|
|
408
|
-
logger.debug(`Cache cleanup: removed ${cleaned} expired entries`);
|
|
409
|
-
}
|
|
410
|
-
// Clean up tag associations for deleted keys
|
|
411
|
-
this.tags.forEach((keys, tag) => {
|
|
412
|
-
const validKeys = new Set(Array.from(keys).filter((key) => this.cache.has(key)));
|
|
413
|
-
if (validKeys.size !== keys.size) {
|
|
414
|
-
this.tags.set(tag, validKeys);
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
/**
|
|
419
|
-
* Get cache hit ratio
|
|
420
|
-
*/
|
|
421
|
-
getHitRatio() {
|
|
422
|
-
const total = this.statsData.hits + this.statsData.misses;
|
|
423
|
-
return total > 0 ? this.statsData.hits / total : 0;
|
|
424
|
-
}
|
|
425
|
-
/**
|
|
426
|
-
* Export cache state for debugging
|
|
427
|
-
*/
|
|
428
|
-
debug() {
|
|
429
|
-
return {
|
|
430
|
-
stats: this.stats(),
|
|
431
|
-
hitRatio: this.getHitRatio(),
|
|
432
|
-
detailedStats: this.getDetailedStats(),
|
|
433
|
-
keys: this.keys(),
|
|
434
|
-
tags: Object.fromEntries(this.tags.entries()),
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
// Export singleton instance
|
|
439
|
-
export const cache = new Cache();
|
|
440
|
-
// Export factory function
|
|
441
|
-
export function createCache(options) {
|
|
442
|
-
return new Cache(options);
|
|
443
|
-
}
|
|
444
|
-
//# sourceMappingURL=Cache.js.map
|