@juspay/neurolink 7.1.0 → 7.3.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 +15 -2
- package/README.md +16 -11
- package/dist/cli/commands/config.d.ts +2 -2
- package/dist/cli/commands/config.js +22 -21
- package/dist/cli/commands/mcp.d.ts +79 -0
- package/dist/cli/commands/mcp.js +916 -0
- package/dist/cli/commands/models.d.ts +63 -0
- package/dist/cli/commands/models.js +653 -0
- package/dist/cli/commands/ollama.js +56 -55
- package/dist/cli/factories/commandFactory.d.ts +14 -0
- package/dist/cli/factories/commandFactory.js +346 -47
- package/dist/cli/index.js +25 -10
- package/dist/cli/utils/completeSetup.js +9 -8
- package/dist/cli/utils/envManager.js +7 -6
- package/dist/cli/utils/interactiveSetup.js +20 -19
- package/dist/core/analytics.js +25 -38
- package/dist/core/baseProvider.d.ts +8 -0
- package/dist/core/baseProvider.js +177 -68
- package/dist/core/constants.d.ts +11 -0
- package/dist/core/constants.js +17 -0
- package/dist/core/evaluation.js +25 -14
- package/dist/core/factory.js +19 -18
- package/dist/core/streamAnalytics.d.ts +65 -0
- package/dist/core/streamAnalytics.js +125 -0
- package/dist/lib/core/analytics.js +25 -38
- package/dist/lib/core/baseProvider.d.ts +8 -0
- package/dist/lib/core/baseProvider.js +177 -68
- package/dist/lib/core/constants.d.ts +11 -0
- package/dist/lib/core/constants.js +17 -0
- package/dist/lib/core/evaluation.js +25 -14
- package/dist/lib/core/factory.js +19 -18
- package/dist/lib/core/streamAnalytics.d.ts +65 -0
- package/dist/lib/core/streamAnalytics.js +125 -0
- package/dist/lib/models/modelRegistry.d.ts +132 -0
- package/dist/lib/models/modelRegistry.js +483 -0
- package/dist/lib/models/modelResolver.d.ts +115 -0
- package/dist/lib/models/modelResolver.js +467 -0
- package/dist/lib/neurolink.d.ts +4 -1
- package/dist/lib/neurolink.js +101 -67
- package/dist/lib/providers/anthropic.js +3 -0
- package/dist/lib/providers/googleAiStudio.js +13 -0
- package/dist/lib/providers/huggingFace.js +15 -3
- package/dist/lib/providers/mistral.js +19 -7
- package/dist/lib/providers/ollama.js +31 -7
- package/dist/lib/providers/openAI.js +12 -0
- package/dist/lib/sdk/toolRegistration.js +2 -2
- package/dist/lib/types/cli.d.ts +56 -1
- package/dist/lib/types/contextTypes.d.ts +110 -0
- package/dist/lib/types/contextTypes.js +176 -0
- package/dist/lib/types/index.d.ts +4 -1
- package/dist/lib/types/mcpTypes.d.ts +118 -7
- package/dist/lib/types/providers.d.ts +81 -0
- package/dist/lib/types/streamTypes.d.ts +44 -7
- package/dist/lib/types/tools.d.ts +9 -0
- package/dist/lib/types/universalProviderOptions.d.ts +3 -1
- package/dist/lib/types/universalProviderOptions.js +2 -1
- package/dist/lib/utils/logger.d.ts +7 -0
- package/dist/lib/utils/logger.js +11 -0
- package/dist/lib/utils/performance.d.ts +105 -0
- package/dist/lib/utils/performance.js +210 -0
- package/dist/lib/utils/retryHandler.d.ts +89 -0
- package/dist/lib/utils/retryHandler.js +269 -0
- package/dist/models/modelRegistry.d.ts +132 -0
- package/dist/models/modelRegistry.js +483 -0
- package/dist/models/modelResolver.d.ts +115 -0
- package/dist/models/modelResolver.js +468 -0
- package/dist/neurolink.d.ts +4 -1
- package/dist/neurolink.js +101 -67
- package/dist/providers/anthropic.js +3 -0
- package/dist/providers/googleAiStudio.js +13 -0
- package/dist/providers/huggingFace.js +15 -3
- package/dist/providers/mistral.js +19 -7
- package/dist/providers/ollama.js +31 -7
- package/dist/providers/openAI.js +12 -0
- package/dist/sdk/toolRegistration.js +2 -2
- package/dist/types/cli.d.ts +56 -1
- package/dist/types/contextTypes.d.ts +110 -0
- package/dist/types/contextTypes.js +177 -0
- package/dist/types/index.d.ts +4 -1
- package/dist/types/mcpTypes.d.ts +118 -7
- package/dist/types/providers.d.ts +81 -0
- package/dist/types/streamTypes.d.ts +44 -7
- package/dist/types/tools.d.ts +9 -0
- package/dist/types/universalProviderOptions.d.ts +3 -1
- package/dist/types/universalProviderOptions.js +3 -1
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.js +11 -0
- package/dist/utils/performance.d.ts +105 -0
- package/dist/utils/performance.js +210 -0
- package/dist/utils/retryHandler.d.ts +89 -0
- package/dist/utils/retryHandler.js +269 -0
- package/package.json +2 -1
package/dist/lib/utils/logger.js
CHANGED
|
@@ -121,6 +121,14 @@ class NeuroLinkLogger {
|
|
|
121
121
|
always(...args) {
|
|
122
122
|
console.log(...args);
|
|
123
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Displays tabular data unconditionally using `console.table`.
|
|
126
|
+
*
|
|
127
|
+
* @param data - The data to display in table format
|
|
128
|
+
*/
|
|
129
|
+
table(data) {
|
|
130
|
+
console.table(data);
|
|
131
|
+
}
|
|
124
132
|
}
|
|
125
133
|
// Export singleton instance
|
|
126
134
|
const neuroLinkLogger = new NeuroLinkLogger();
|
|
@@ -166,6 +174,9 @@ export const logger = {
|
|
|
166
174
|
always: (...args) => {
|
|
167
175
|
neuroLinkLogger.always(...args);
|
|
168
176
|
},
|
|
177
|
+
table: (data) => {
|
|
178
|
+
neuroLinkLogger.table(data);
|
|
179
|
+
},
|
|
169
180
|
// Expose structured logging methods
|
|
170
181
|
setLogLevel: (level) => neuroLinkLogger.setLogLevel(level),
|
|
171
182
|
getLogs: (level) => neuroLinkLogger.getLogs(level),
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance measurement and memory management utilities
|
|
3
|
+
* Part of Sub-phase 3.3.1-3.3.2 optimization efforts
|
|
4
|
+
*/
|
|
5
|
+
export interface PerformanceMetrics {
|
|
6
|
+
startTime: number;
|
|
7
|
+
endTime?: number;
|
|
8
|
+
duration?: number;
|
|
9
|
+
memoryStart: NodeJS.MemoryUsage;
|
|
10
|
+
memoryEnd?: NodeJS.MemoryUsage;
|
|
11
|
+
memoryDelta?: {
|
|
12
|
+
rss: number;
|
|
13
|
+
heapTotal: number;
|
|
14
|
+
heapUsed: number;
|
|
15
|
+
external: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Performance measurement utility for tracking operations
|
|
20
|
+
*/
|
|
21
|
+
export declare class PerformanceTracker {
|
|
22
|
+
private metrics;
|
|
23
|
+
/**
|
|
24
|
+
* Start tracking performance for an operation
|
|
25
|
+
*/
|
|
26
|
+
start(operationName: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* End tracking and calculate metrics
|
|
29
|
+
*/
|
|
30
|
+
end(operationName: string): PerformanceMetrics | null;
|
|
31
|
+
/**
|
|
32
|
+
* Get metrics for an operation
|
|
33
|
+
*/
|
|
34
|
+
getMetrics(operationName: string): PerformanceMetrics | null;
|
|
35
|
+
/**
|
|
36
|
+
* Clear all metrics
|
|
37
|
+
*/
|
|
38
|
+
clear(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Format metrics for display
|
|
41
|
+
*/
|
|
42
|
+
formatMetrics(operationName: string): string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Global performance tracker instance
|
|
46
|
+
*/
|
|
47
|
+
export declare const globalTracker: PerformanceTracker;
|
|
48
|
+
/**
|
|
49
|
+
* Memory management utilities
|
|
50
|
+
*/
|
|
51
|
+
export declare class MemoryManager {
|
|
52
|
+
/**
|
|
53
|
+
* Force garbage collection if available
|
|
54
|
+
*/
|
|
55
|
+
static forceGC(): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Get current memory usage in MB
|
|
58
|
+
*/
|
|
59
|
+
static getMemoryUsageMB(): {
|
|
60
|
+
rss: number;
|
|
61
|
+
heapTotal: number;
|
|
62
|
+
heapUsed: number;
|
|
63
|
+
external: number;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Monitor memory usage and warn if it exceeds threshold
|
|
67
|
+
*/
|
|
68
|
+
static monitorMemory(threshold?: number): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Clean up and optimize memory usage.
|
|
71
|
+
* Attempts to force garbage collection if available.
|
|
72
|
+
*
|
|
73
|
+
* @returns {object|null} Memory usage statistics if cleanup was performed, or null if not possible.
|
|
74
|
+
* - If manual garbage collection is not available (i.e., Node.js not run with --expose-gc),
|
|
75
|
+
* no cleanup is performed and null is returned.
|
|
76
|
+
* - Clearing the require cache is not attempted due to potential side effects.
|
|
77
|
+
*/
|
|
78
|
+
static cleanup(): {
|
|
79
|
+
beforeMB: number;
|
|
80
|
+
afterMB: number;
|
|
81
|
+
freedMB: number;
|
|
82
|
+
} | null;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Decorator for tracking performance of async functions
|
|
86
|
+
*/
|
|
87
|
+
export declare function trackPerformance(operationName: string): <T extends (...args: unknown[]) => Promise<unknown>>(target: unknown, propertyName: string, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T>;
|
|
88
|
+
/**
|
|
89
|
+
* Performance monitoring for CLI operations
|
|
90
|
+
*/
|
|
91
|
+
export declare class CLIPerformanceMonitor {
|
|
92
|
+
private static instance;
|
|
93
|
+
private enabled;
|
|
94
|
+
static getInstance(): CLIPerformanceMonitor;
|
|
95
|
+
enable(): void;
|
|
96
|
+
disable(): void;
|
|
97
|
+
/**
|
|
98
|
+
* Monitor a CLI operation
|
|
99
|
+
*/
|
|
100
|
+
monitorOperation<T>(operationName: string, operation: () => Promise<T>): Promise<T>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Export singleton monitor for easy access
|
|
104
|
+
*/
|
|
105
|
+
export declare const cliMonitor: CLIPerformanceMonitor;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance measurement and memory management utilities
|
|
3
|
+
* Part of Sub-phase 3.3.1-3.3.2 optimization efforts
|
|
4
|
+
*/
|
|
5
|
+
import { logger } from "./logger.js";
|
|
6
|
+
/**
|
|
7
|
+
* Performance measurement utility for tracking operations
|
|
8
|
+
*/
|
|
9
|
+
export class PerformanceTracker {
|
|
10
|
+
metrics = new Map();
|
|
11
|
+
/**
|
|
12
|
+
* Start tracking performance for an operation
|
|
13
|
+
*/
|
|
14
|
+
start(operationName) {
|
|
15
|
+
this.metrics.set(operationName, {
|
|
16
|
+
startTime: Date.now(),
|
|
17
|
+
memoryStart: process.memoryUsage(),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* End tracking and calculate metrics
|
|
22
|
+
*/
|
|
23
|
+
end(operationName) {
|
|
24
|
+
const metric = this.metrics.get(operationName);
|
|
25
|
+
if (!metric) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const endTime = Date.now();
|
|
29
|
+
const memoryEnd = process.memoryUsage();
|
|
30
|
+
const completedMetric = {
|
|
31
|
+
...metric,
|
|
32
|
+
endTime,
|
|
33
|
+
duration: endTime - metric.startTime,
|
|
34
|
+
memoryEnd,
|
|
35
|
+
memoryDelta: {
|
|
36
|
+
rss: memoryEnd.rss - metric.memoryStart.rss,
|
|
37
|
+
heapTotal: memoryEnd.heapTotal - metric.memoryStart.heapTotal,
|
|
38
|
+
heapUsed: memoryEnd.heapUsed - metric.memoryStart.heapUsed,
|
|
39
|
+
external: memoryEnd.external - metric.memoryStart.external,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
this.metrics.set(operationName, completedMetric);
|
|
43
|
+
return completedMetric;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get metrics for an operation
|
|
47
|
+
*/
|
|
48
|
+
getMetrics(operationName) {
|
|
49
|
+
return this.metrics.get(operationName) || null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Clear all metrics
|
|
53
|
+
*/
|
|
54
|
+
clear() {
|
|
55
|
+
this.metrics.clear();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Format metrics for display
|
|
59
|
+
*/
|
|
60
|
+
formatMetrics(operationName) {
|
|
61
|
+
const metric = this.metrics.get(operationName);
|
|
62
|
+
if (!metric || !metric.duration) {
|
|
63
|
+
return `${operationName}: No metrics available`;
|
|
64
|
+
}
|
|
65
|
+
const memoryMB = (bytes) => (bytes / 1024 / 1024).toFixed(1);
|
|
66
|
+
return [
|
|
67
|
+
`${operationName}:`,
|
|
68
|
+
` Duration: ${metric.duration}ms`,
|
|
69
|
+
` Memory Delta: +${memoryMB(metric.memoryDelta.heapUsed)}MB heap`,
|
|
70
|
+
` RSS Delta: +${memoryMB(metric.memoryDelta.rss)}MB`,
|
|
71
|
+
].join("\n");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Global performance tracker instance
|
|
76
|
+
*/
|
|
77
|
+
export const globalTracker = new PerformanceTracker();
|
|
78
|
+
/**
|
|
79
|
+
* Memory management utilities
|
|
80
|
+
*/
|
|
81
|
+
export class MemoryManager {
|
|
82
|
+
/**
|
|
83
|
+
* Force garbage collection if available
|
|
84
|
+
*/
|
|
85
|
+
static forceGC() {
|
|
86
|
+
if (typeof global !== "undefined" && global.gc) {
|
|
87
|
+
global.gc();
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get current memory usage in MB
|
|
94
|
+
*/
|
|
95
|
+
static getMemoryUsageMB() {
|
|
96
|
+
const usage = process.memoryUsage();
|
|
97
|
+
return {
|
|
98
|
+
rss: Math.round(usage.rss / 1024 / 1024),
|
|
99
|
+
heapTotal: Math.round(usage.heapTotal / 1024 / 1024),
|
|
100
|
+
heapUsed: Math.round(usage.heapUsed / 1024 / 1024),
|
|
101
|
+
external: Math.round(usage.external / 1024 / 1024),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Monitor memory usage and warn if it exceeds threshold
|
|
106
|
+
*/
|
|
107
|
+
static monitorMemory(threshold = 100) {
|
|
108
|
+
const usage = this.getMemoryUsageMB();
|
|
109
|
+
if (usage.heapUsed > threshold) {
|
|
110
|
+
logger.warn(`⚠️ High memory usage: ${usage.heapUsed}MB heap (threshold: ${threshold}MB)`);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Clean up and optimize memory usage.
|
|
117
|
+
* Attempts to force garbage collection if available.
|
|
118
|
+
*
|
|
119
|
+
* @returns {object|null} Memory usage statistics if cleanup was performed, or null if not possible.
|
|
120
|
+
* - If manual garbage collection is not available (i.e., Node.js not run with --expose-gc),
|
|
121
|
+
* no cleanup is performed and null is returned.
|
|
122
|
+
* - Clearing the require cache is not attempted due to potential side effects.
|
|
123
|
+
*/
|
|
124
|
+
static cleanup() {
|
|
125
|
+
const before = this.getMemoryUsageMB();
|
|
126
|
+
const gcForced = this.forceGC();
|
|
127
|
+
if (!gcForced) {
|
|
128
|
+
// Manual garbage collection not available.
|
|
129
|
+
// No cleanup performed. Clearing require cache is dangerous and not attempted.
|
|
130
|
+
// Memory cleanup relies on Node.js natural garbage collection.
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const after = this.getMemoryUsageMB();
|
|
134
|
+
return {
|
|
135
|
+
beforeMB: before.heapUsed,
|
|
136
|
+
afterMB: after.heapUsed,
|
|
137
|
+
freedMB: before.heapUsed - after.heapUsed,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Decorator for tracking performance of async functions
|
|
143
|
+
*/
|
|
144
|
+
export function trackPerformance(operationName) {
|
|
145
|
+
return function (target, propertyName, descriptor) {
|
|
146
|
+
const method = descriptor.value;
|
|
147
|
+
descriptor.value = async function (...args) {
|
|
148
|
+
globalTracker.start(operationName);
|
|
149
|
+
try {
|
|
150
|
+
const result = await method.apply(this, args);
|
|
151
|
+
globalTracker.end(operationName);
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
globalTracker.end(operationName);
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
return descriptor;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Performance monitoring for CLI operations
|
|
164
|
+
*/
|
|
165
|
+
export class CLIPerformanceMonitor {
|
|
166
|
+
static instance;
|
|
167
|
+
enabled = false;
|
|
168
|
+
static getInstance() {
|
|
169
|
+
if (!CLIPerformanceMonitor.instance) {
|
|
170
|
+
CLIPerformanceMonitor.instance = new CLIPerformanceMonitor();
|
|
171
|
+
}
|
|
172
|
+
return CLIPerformanceMonitor.instance;
|
|
173
|
+
}
|
|
174
|
+
enable() {
|
|
175
|
+
this.enabled = true;
|
|
176
|
+
}
|
|
177
|
+
disable() {
|
|
178
|
+
this.enabled = false;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Monitor a CLI operation
|
|
182
|
+
*/
|
|
183
|
+
async monitorOperation(operationName, operation) {
|
|
184
|
+
if (!this.enabled) {
|
|
185
|
+
return operation();
|
|
186
|
+
}
|
|
187
|
+
globalTracker.start(operationName);
|
|
188
|
+
const startMemory = MemoryManager.getMemoryUsageMB();
|
|
189
|
+
try {
|
|
190
|
+
const result = await operation();
|
|
191
|
+
const metrics = globalTracker.end(operationName);
|
|
192
|
+
const endMemory = MemoryManager.getMemoryUsageMB();
|
|
193
|
+
if (metrics) {
|
|
194
|
+
logger.debug(`\n🔍 Performance: ${operationName}`);
|
|
195
|
+
logger.debug(` Duration: ${metrics.duration}ms`);
|
|
196
|
+
logger.debug(` Memory: ${startMemory.heapUsed}MB → ${endMemory.heapUsed}MB`);
|
|
197
|
+
logger.debug(` Delta: +${endMemory.heapUsed - startMemory.heapUsed}MB`);
|
|
198
|
+
}
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
globalTracker.end(operationName);
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Export singleton monitor for easy access
|
|
209
|
+
*/
|
|
210
|
+
export const cliMonitor = CLIPerformanceMonitor.getInstance();
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry and resilience utilities for NeuroLink
|
|
3
|
+
* Part of Sub-phase 3.3.3 - Edge Case Handling
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Calculate exponential backoff delay with jitter
|
|
7
|
+
* @param attempt - Current attempt number (1-based)
|
|
8
|
+
* @param initialDelay - Initial delay in milliseconds
|
|
9
|
+
* @param multiplier - Backoff multiplier for exponential growth
|
|
10
|
+
* @param maxDelay - Maximum delay cap in milliseconds
|
|
11
|
+
* @param addJitter - Whether to add random jitter to prevent thundering herd
|
|
12
|
+
* @returns Calculated delay in milliseconds
|
|
13
|
+
*/
|
|
14
|
+
export declare function calculateBackoffDelay(attempt: number, initialDelay?: number, multiplier?: number, maxDelay?: number, addJitter?: boolean): number;
|
|
15
|
+
export interface RetryOptions {
|
|
16
|
+
maxAttempts?: number;
|
|
17
|
+
initialDelay?: number;
|
|
18
|
+
maxDelay?: number;
|
|
19
|
+
backoffMultiplier?: number;
|
|
20
|
+
retryCondition?: (error: unknown) => boolean;
|
|
21
|
+
onRetry?: (attempt: number, error: unknown) => void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Error types that are typically retryable
|
|
25
|
+
*/
|
|
26
|
+
export declare class NetworkError extends Error {
|
|
27
|
+
readonly cause?: Error | undefined;
|
|
28
|
+
constructor(message: string, cause?: Error | undefined);
|
|
29
|
+
}
|
|
30
|
+
export declare class TemporaryError extends Error {
|
|
31
|
+
readonly cause?: Error | undefined;
|
|
32
|
+
constructor(message: string, cause?: Error | undefined);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Default retry configuration
|
|
36
|
+
*/
|
|
37
|
+
export declare const DEFAULT_RETRY_CONFIG: Required<RetryOptions>;
|
|
38
|
+
/**
|
|
39
|
+
* Execute an operation with retry logic
|
|
40
|
+
*/
|
|
41
|
+
export declare function withRetry<T>(operation: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
42
|
+
/**
|
|
43
|
+
* Enhanced timeout with retry for network operations
|
|
44
|
+
*/
|
|
45
|
+
export declare function withTimeoutAndRetry<T>(operation: () => Promise<T>, timeoutMs: number, retryOptions?: RetryOptions): Promise<T>;
|
|
46
|
+
/**
|
|
47
|
+
* Circuit breaker pattern for preventing cascading failures
|
|
48
|
+
*/
|
|
49
|
+
export declare class CircuitBreaker {
|
|
50
|
+
private threshold;
|
|
51
|
+
private timeout;
|
|
52
|
+
private monitorWindow;
|
|
53
|
+
private failures;
|
|
54
|
+
private lastFailureTime;
|
|
55
|
+
private state;
|
|
56
|
+
constructor(threshold?: number, timeout?: number, // 1 minute
|
|
57
|
+
monitorWindow?: number);
|
|
58
|
+
execute<T>(operation: () => Promise<T>): Promise<T>;
|
|
59
|
+
private onSuccess;
|
|
60
|
+
private onFailure;
|
|
61
|
+
getState(): string;
|
|
62
|
+
reset(): void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Rate limiter to prevent overwhelming APIs
|
|
66
|
+
*/
|
|
67
|
+
export declare class RateLimiter {
|
|
68
|
+
private maxRequests;
|
|
69
|
+
private windowMs;
|
|
70
|
+
private requests;
|
|
71
|
+
constructor(maxRequests: number, windowMs: number);
|
|
72
|
+
acquire(): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Utility for graceful shutdown handling
|
|
76
|
+
*/
|
|
77
|
+
export declare class GracefulShutdown {
|
|
78
|
+
private operations;
|
|
79
|
+
private shutdownPromise;
|
|
80
|
+
track<T>(operation: Promise<T>): Promise<T>;
|
|
81
|
+
shutdown(timeoutMs?: number): Promise<void>;
|
|
82
|
+
private performShutdown;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Global instances for convenience
|
|
86
|
+
*/
|
|
87
|
+
export declare const globalShutdown: GracefulShutdown;
|
|
88
|
+
export declare const providerCircuitBreaker: CircuitBreaker;
|
|
89
|
+
export declare const apiRateLimiter: RateLimiter;
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry and resilience utilities for NeuroLink
|
|
3
|
+
* Part of Sub-phase 3.3.3 - Edge Case Handling
|
|
4
|
+
*/
|
|
5
|
+
import { logger } from "./logger.js";
|
|
6
|
+
import { SYSTEM_LIMITS } from "../core/constants.js";
|
|
7
|
+
/**
|
|
8
|
+
* Calculate exponential backoff delay with jitter
|
|
9
|
+
* @param attempt - Current attempt number (1-based)
|
|
10
|
+
* @param initialDelay - Initial delay in milliseconds
|
|
11
|
+
* @param multiplier - Backoff multiplier for exponential growth
|
|
12
|
+
* @param maxDelay - Maximum delay cap in milliseconds
|
|
13
|
+
* @param addJitter - Whether to add random jitter to prevent thundering herd
|
|
14
|
+
* @returns Calculated delay in milliseconds
|
|
15
|
+
*/
|
|
16
|
+
export function calculateBackoffDelay(attempt, initialDelay = SYSTEM_LIMITS.DEFAULT_INITIAL_DELAY, multiplier = SYSTEM_LIMITS.DEFAULT_BACKOFF_MULTIPLIER, maxDelay = SYSTEM_LIMITS.DEFAULT_MAX_DELAY, addJitter = true) {
|
|
17
|
+
// Calculate exponential backoff
|
|
18
|
+
const exponentialDelay = initialDelay * Math.pow(multiplier, attempt - 1);
|
|
19
|
+
// Apply maximum delay cap
|
|
20
|
+
const cappedDelay = Math.min(exponentialDelay, maxDelay);
|
|
21
|
+
// Add jitter to avoid thundering herd (up to 10% of delay, max 1 second)
|
|
22
|
+
const jitter = addJitter
|
|
23
|
+
? Math.random() * Math.min(cappedDelay * 0.1, 1000)
|
|
24
|
+
: 0;
|
|
25
|
+
return cappedDelay + jitter;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Error types that are typically retryable
|
|
29
|
+
*/
|
|
30
|
+
export class NetworkError extends Error {
|
|
31
|
+
cause;
|
|
32
|
+
constructor(message, cause) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.cause = cause;
|
|
35
|
+
this.name = "NetworkError";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export class TemporaryError extends Error {
|
|
39
|
+
cause;
|
|
40
|
+
constructor(message, cause) {
|
|
41
|
+
super(message);
|
|
42
|
+
this.cause = cause;
|
|
43
|
+
this.name = "TemporaryError";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Default retry configuration
|
|
48
|
+
*/
|
|
49
|
+
export const DEFAULT_RETRY_CONFIG = {
|
|
50
|
+
maxAttempts: SYSTEM_LIMITS.DEFAULT_RETRY_ATTEMPTS,
|
|
51
|
+
initialDelay: SYSTEM_LIMITS.DEFAULT_INITIAL_DELAY,
|
|
52
|
+
maxDelay: SYSTEM_LIMITS.DEFAULT_MAX_DELAY,
|
|
53
|
+
backoffMultiplier: SYSTEM_LIMITS.DEFAULT_BACKOFF_MULTIPLIER,
|
|
54
|
+
retryCondition: (error) => {
|
|
55
|
+
// Retry on network errors, timeouts, and specific HTTP errors
|
|
56
|
+
if (error instanceof NetworkError || error instanceof TemporaryError) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
// Retry on timeout errors
|
|
60
|
+
if (error &&
|
|
61
|
+
typeof error === "object" &&
|
|
62
|
+
(error.name === "TimeoutError" ||
|
|
63
|
+
error.code === "TIMEOUT")) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
// Retry on network-related errors
|
|
67
|
+
if (error &&
|
|
68
|
+
typeof error === "object" &&
|
|
69
|
+
(error.code === "ECONNRESET" ||
|
|
70
|
+
error.code === "ENOTFOUND" ||
|
|
71
|
+
error.code === "ECONNREFUSED" ||
|
|
72
|
+
error.code === "ETIMEDOUT")) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
// Retry on HTTP 5xx errors and some 4xx errors
|
|
76
|
+
if (error &&
|
|
77
|
+
typeof error === "object" &&
|
|
78
|
+
error.status) {
|
|
79
|
+
const status = Number(error.status);
|
|
80
|
+
return status >= 500 || status === 429 || status === 408;
|
|
81
|
+
}
|
|
82
|
+
// Don't retry by default
|
|
83
|
+
return false;
|
|
84
|
+
},
|
|
85
|
+
onRetry: (attempt, error) => {
|
|
86
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
87
|
+
logger.warn(`⚠️ Retry attempt ${attempt}: ${message}`);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Sleep utility for retry delays
|
|
92
|
+
*/
|
|
93
|
+
function sleep(ms) {
|
|
94
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Execute an operation with retry logic
|
|
98
|
+
*/
|
|
99
|
+
export async function withRetry(operation, options = {}) {
|
|
100
|
+
const config = { ...DEFAULT_RETRY_CONFIG, ...options };
|
|
101
|
+
let lastError;
|
|
102
|
+
for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
|
|
103
|
+
try {
|
|
104
|
+
return await operation();
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
lastError = error;
|
|
108
|
+
// Don't retry if it's the last attempt
|
|
109
|
+
if (attempt === config.maxAttempts) {
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
// Check if we should retry this error
|
|
113
|
+
if (!config.retryCondition(error)) {
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
// Call retry callback
|
|
117
|
+
config.onRetry(attempt, error);
|
|
118
|
+
// Calculate delay with exponential backoff and jitter
|
|
119
|
+
const jitteredDelay = calculateBackoffDelay(attempt, config.initialDelay, config.backoffMultiplier, config.maxDelay, true);
|
|
120
|
+
await sleep(jitteredDelay);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
throw lastError;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Enhanced timeout with retry for network operations
|
|
127
|
+
*/
|
|
128
|
+
export async function withTimeoutAndRetry(operation, timeoutMs, retryOptions = {}) {
|
|
129
|
+
return withRetry(async () => {
|
|
130
|
+
return new Promise((resolve, reject) => {
|
|
131
|
+
const timeout = setTimeout(() => {
|
|
132
|
+
reject(new NetworkError(`Operation timed out after ${timeoutMs}ms`));
|
|
133
|
+
}, timeoutMs);
|
|
134
|
+
operation()
|
|
135
|
+
.then((result) => {
|
|
136
|
+
clearTimeout(timeout);
|
|
137
|
+
resolve(result);
|
|
138
|
+
})
|
|
139
|
+
.catch((error) => {
|
|
140
|
+
clearTimeout(timeout);
|
|
141
|
+
reject(error);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}, retryOptions);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Circuit breaker pattern for preventing cascading failures
|
|
148
|
+
*/
|
|
149
|
+
export class CircuitBreaker {
|
|
150
|
+
threshold;
|
|
151
|
+
timeout;
|
|
152
|
+
monitorWindow;
|
|
153
|
+
failures = 0;
|
|
154
|
+
lastFailureTime = 0;
|
|
155
|
+
state = "closed";
|
|
156
|
+
constructor(threshold = 5, timeout = 60000, // 1 minute
|
|
157
|
+
monitorWindow = 600000) {
|
|
158
|
+
this.threshold = threshold;
|
|
159
|
+
this.timeout = timeout;
|
|
160
|
+
this.monitorWindow = monitorWindow;
|
|
161
|
+
}
|
|
162
|
+
async execute(operation) {
|
|
163
|
+
if (this.state === "open") {
|
|
164
|
+
if (Date.now() - this.lastFailureTime > this.timeout) {
|
|
165
|
+
this.state = "half-open";
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
throw new Error("Circuit breaker is open - operation rejected");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
const result = await operation();
|
|
173
|
+
this.onSuccess();
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
this.onFailure();
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
onSuccess() {
|
|
182
|
+
this.failures = 0;
|
|
183
|
+
this.state = "closed";
|
|
184
|
+
}
|
|
185
|
+
onFailure() {
|
|
186
|
+
this.failures++;
|
|
187
|
+
this.lastFailureTime = Date.now();
|
|
188
|
+
if (this.failures >= this.threshold) {
|
|
189
|
+
this.state = "open";
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
getState() {
|
|
193
|
+
return this.state;
|
|
194
|
+
}
|
|
195
|
+
reset() {
|
|
196
|
+
this.failures = 0;
|
|
197
|
+
this.lastFailureTime = 0;
|
|
198
|
+
this.state = "closed";
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Rate limiter to prevent overwhelming APIs
|
|
203
|
+
*/
|
|
204
|
+
export class RateLimiter {
|
|
205
|
+
maxRequests;
|
|
206
|
+
windowMs;
|
|
207
|
+
requests = [];
|
|
208
|
+
constructor(maxRequests, windowMs) {
|
|
209
|
+
this.maxRequests = maxRequests;
|
|
210
|
+
this.windowMs = windowMs;
|
|
211
|
+
}
|
|
212
|
+
async acquire() {
|
|
213
|
+
const now = Date.now();
|
|
214
|
+
// Remove old requests outside the window
|
|
215
|
+
this.requests = this.requests.filter((time) => now - time < this.windowMs);
|
|
216
|
+
if (this.requests.length >= this.maxRequests) {
|
|
217
|
+
// Calculate delay until next available slot
|
|
218
|
+
const oldestRequest = Math.min(...this.requests);
|
|
219
|
+
const delay = this.windowMs - (now - oldestRequest);
|
|
220
|
+
if (delay > 0) {
|
|
221
|
+
await sleep(delay);
|
|
222
|
+
return this.acquire(); // Try again after delay
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
this.requests.push(now);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Utility for graceful shutdown handling
|
|
230
|
+
*/
|
|
231
|
+
export class GracefulShutdown {
|
|
232
|
+
operations = new Set();
|
|
233
|
+
shutdownPromise = null;
|
|
234
|
+
track(operation) {
|
|
235
|
+
this.operations.add(operation);
|
|
236
|
+
operation.finally(() => {
|
|
237
|
+
this.operations.delete(operation);
|
|
238
|
+
});
|
|
239
|
+
return operation;
|
|
240
|
+
}
|
|
241
|
+
async shutdown(timeoutMs = 30000) {
|
|
242
|
+
if (this.shutdownPromise) {
|
|
243
|
+
return this.shutdownPromise;
|
|
244
|
+
}
|
|
245
|
+
this.shutdownPromise = this.performShutdown(timeoutMs);
|
|
246
|
+
return this.shutdownPromise;
|
|
247
|
+
}
|
|
248
|
+
async performShutdown(timeoutMs) {
|
|
249
|
+
logger.debug(`🔄 Graceful shutdown: waiting for ${this.operations.size} operations...`);
|
|
250
|
+
try {
|
|
251
|
+
await Promise.race([
|
|
252
|
+
Promise.all(this.operations),
|
|
253
|
+
sleep(timeoutMs).then(() => {
|
|
254
|
+
throw new Error(`Shutdown timeout: ${this.operations.size} operations still running`);
|
|
255
|
+
}),
|
|
256
|
+
]);
|
|
257
|
+
logger.debug("✅ Graceful shutdown completed");
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
logger.warn(`⚠️ Shutdown warning: ${error instanceof Error ? error.message : String(error)}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Global instances for convenience
|
|
266
|
+
*/
|
|
267
|
+
export const globalShutdown = new GracefulShutdown();
|
|
268
|
+
export const providerCircuitBreaker = new CircuitBreaker(3, 30000); // 3 failures, 30s timeout
|
|
269
|
+
export const apiRateLimiter = new RateLimiter(100, 60000); // 100 requests per minute
|