@push.rocks/smartproxy 19.6.2 → 19.6.7

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 (52) hide show
  1. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +4 -7
  2. package/dist_ts/proxies/smart-proxy/connection-manager.js +22 -22
  3. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +4 -3
  4. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +9 -9
  5. package/dist_ts/proxies/smart-proxy/metrics-collector.d.ts +68 -56
  6. package/dist_ts/proxies/smart-proxy/metrics-collector.js +226 -176
  7. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -0
  8. package/dist_ts/proxies/smart-proxy/models/metrics-types.d.ts +94 -48
  9. package/dist_ts/proxies/smart-proxy/nftables-manager.d.ts +4 -4
  10. package/dist_ts/proxies/smart-proxy/nftables-manager.js +6 -6
  11. package/dist_ts/proxies/smart-proxy/port-manager.d.ts +4 -7
  12. package/dist_ts/proxies/smart-proxy/port-manager.js +6 -9
  13. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +4 -15
  14. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +128 -128
  15. package/dist_ts/proxies/smart-proxy/security-manager.d.ts +3 -3
  16. package/dist_ts/proxies/smart-proxy/security-manager.js +9 -9
  17. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +20 -13
  18. package/dist_ts/proxies/smart-proxy/smart-proxy.js +16 -13
  19. package/dist_ts/proxies/smart-proxy/throughput-tracker.d.ts +36 -0
  20. package/dist_ts/proxies/smart-proxy/throughput-tracker.js +117 -0
  21. package/dist_ts/proxies/smart-proxy/timeout-manager.d.ts +4 -3
  22. package/dist_ts/proxies/smart-proxy/timeout-manager.js +16 -16
  23. package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +3 -3
  24. package/dist_ts/proxies/smart-proxy/tls-manager.js +12 -12
  25. package/package.json +8 -17
  26. package/readme.hints.md +0 -897
  27. package/readme.md +960 -54
  28. package/readme.plan.md +301 -562
  29. package/ts/proxies/smart-proxy/connection-manager.ts +23 -21
  30. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +9 -8
  31. package/ts/proxies/smart-proxy/metrics-collector.ts +277 -189
  32. package/ts/proxies/smart-proxy/models/interfaces.ts +7 -0
  33. package/ts/proxies/smart-proxy/models/metrics-types.ts +93 -41
  34. package/ts/proxies/smart-proxy/nftables-manager.ts +5 -5
  35. package/ts/proxies/smart-proxy/port-manager.ts +6 -14
  36. package/ts/proxies/smart-proxy/route-connection-handler.ts +136 -136
  37. package/ts/proxies/smart-proxy/security-manager.ts +8 -8
  38. package/ts/proxies/smart-proxy/smart-proxy.ts +26 -35
  39. package/ts/proxies/smart-proxy/throughput-tracker.ts +144 -0
  40. package/ts/proxies/smart-proxy/timeout-manager.ts +16 -15
  41. package/ts/proxies/smart-proxy/tls-manager.ts +11 -11
  42. package/readme.connections.md +0 -724
  43. package/readme.delete.md +0 -187
  44. package/readme.memory-leaks-fixed.md +0 -45
  45. package/readme.metrics.md +0 -591
  46. package/readme.monitoring.md +0 -202
  47. package/readme.proxy-chain-summary.md +0 -112
  48. package/readme.proxy-protocol-example.md +0 -462
  49. package/readme.proxy-protocol.md +0 -415
  50. package/readme.routing.md +0 -341
  51. package/readme.websocket-keepalive-config.md +0 -140
  52. package/readme.websocket-keepalive-fix.md +0 -63
@@ -1,211 +1,257 @@
1
1
  import * as plugins from '../../plugins.js';
2
+ import { ThroughputTracker } from './throughput-tracker.js';
2
3
  import { logger } from '../../core/utils/logger.js';
3
4
  /**
4
- * Collects and computes metrics for SmartProxy on-demand
5
+ * Collects and provides metrics for SmartProxy with clean API
5
6
  */
6
7
  export class MetricsCollector {
7
- constructor(smartProxy) {
8
+ constructor(smartProxy, config) {
8
9
  this.smartProxy = smartProxy;
9
- // RPS tracking (the only state we need to maintain)
10
+ // Request tracking
10
11
  this.requestTimestamps = [];
11
- this.RPS_WINDOW_SIZE = 60000; // 1 minute window
12
- this.MAX_TIMESTAMPS = 5000; // Maximum timestamps to keep
13
- // Optional caching for performance
14
- this.cachedMetrics = { timestamp: 0 };
15
- this.CACHE_TTL = 1000; // 1 second cache
16
- // Subscription will be set up in start() method
17
- }
18
- /**
19
- * Get the current number of active connections
20
- */
21
- getActiveConnections() {
22
- return this.smartProxy.connectionManager.getConnectionCount();
23
- }
24
- /**
25
- * Get connection counts grouped by route name
26
- */
27
- getConnectionsByRoute() {
28
- const now = Date.now();
29
- // Return cached value if fresh
30
- if (this.cachedMetrics.connectionsByRoute &&
31
- now - this.cachedMetrics.timestamp < this.CACHE_TTL) {
32
- return new Map(this.cachedMetrics.connectionsByRoute);
33
- }
34
- // Compute fresh value
35
- const routeCounts = new Map();
36
- const connections = this.smartProxy.connectionManager.getConnections();
37
- if (this.smartProxy.settings?.enableDetailedLogging) {
38
- logger.log('debug', `MetricsCollector: Computing route connections`, {
39
- totalConnections: connections.size,
40
- component: 'metrics'
41
- });
42
- }
43
- for (const [_, record] of connections) {
44
- // Try different ways to get the route name
45
- const routeName = record.routeName ||
46
- record.routeConfig?.name ||
47
- record.routeConfig?.routeName ||
48
- 'unknown';
49
- if (this.smartProxy.settings?.enableDetailedLogging) {
50
- logger.log('debug', `MetricsCollector: Connection route info`, {
51
- connectionId: record.id,
52
- routeName,
53
- hasRouteConfig: !!record.routeConfig,
54
- routeConfigName: record.routeConfig?.name,
55
- routeConfigKeys: record.routeConfig ? Object.keys(record.routeConfig) : [],
56
- component: 'metrics'
57
- });
12
+ this.totalRequests = 0;
13
+ // Connection byte tracking for per-route/IP metrics
14
+ this.connectionByteTrackers = new Map();
15
+ // Connection metrics implementation
16
+ this.connections = {
17
+ active: () => {
18
+ return this.smartProxy.connectionManager.getConnectionCount();
19
+ },
20
+ total: () => {
21
+ const stats = this.smartProxy.connectionManager.getTerminationStats();
22
+ let total = this.smartProxy.connectionManager.getConnectionCount();
23
+ for (const reason in stats.incoming) {
24
+ total += stats.incoming[reason];
25
+ }
26
+ return total;
27
+ },
28
+ byRoute: () => {
29
+ const routeCounts = new Map();
30
+ const connections = this.smartProxy.connectionManager.getConnections();
31
+ for (const [_, record] of connections) {
32
+ const routeName = record.routeName ||
33
+ record.routeConfig?.name ||
34
+ 'unknown';
35
+ const current = routeCounts.get(routeName) || 0;
36
+ routeCounts.set(routeName, current + 1);
37
+ }
38
+ return routeCounts;
39
+ },
40
+ byIP: () => {
41
+ const ipCounts = new Map();
42
+ for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
43
+ const ip = record.remoteIP;
44
+ const current = ipCounts.get(ip) || 0;
45
+ ipCounts.set(ip, current + 1);
46
+ }
47
+ return ipCounts;
48
+ },
49
+ topIPs: (limit = 10) => {
50
+ const ipCounts = this.connections.byIP();
51
+ return Array.from(ipCounts.entries())
52
+ .sort((a, b) => b[1] - a[1])
53
+ .slice(0, limit)
54
+ .map(([ip, count]) => ({ ip, count }));
58
55
  }
59
- const current = routeCounts.get(routeName) || 0;
60
- routeCounts.set(routeName, current + 1);
61
- }
62
- // Cache and return
63
- this.cachedMetrics.connectionsByRoute = routeCounts;
64
- this.cachedMetrics.timestamp = now;
65
- return new Map(routeCounts);
66
- }
67
- /**
68
- * Get connection counts grouped by IP address
69
- */
70
- getConnectionsByIP() {
71
- const now = Date.now();
72
- // Return cached value if fresh
73
- if (this.cachedMetrics.connectionsByIP &&
74
- now - this.cachedMetrics.timestamp < this.CACHE_TTL) {
75
- return new Map(this.cachedMetrics.connectionsByIP);
76
- }
77
- // Compute fresh value
78
- const ipCounts = new Map();
79
- for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
80
- const ip = record.remoteIP;
81
- const current = ipCounts.get(ip) || 0;
82
- ipCounts.set(ip, current + 1);
83
- }
84
- // Cache and return
85
- this.cachedMetrics.connectionsByIP = ipCounts;
86
- this.cachedMetrics.timestamp = now;
87
- return new Map(ipCounts);
88
- }
89
- /**
90
- * Get the total number of connections since proxy start
91
- */
92
- getTotalConnections() {
93
- // Get from termination stats
94
- const stats = this.smartProxy.connectionManager.getTerminationStats();
95
- let total = this.smartProxy.connectionManager.getConnectionCount(); // Add active connections
96
- // Add all terminated connections
97
- for (const reason in stats.incoming) {
98
- total += stats.incoming[reason];
99
- }
100
- return total;
101
- }
102
- /**
103
- * Get the current requests per second rate
104
- */
105
- getRequestsPerSecond() {
106
- const now = Date.now();
107
- const windowStart = now - this.RPS_WINDOW_SIZE;
108
- // Clean old timestamps
109
- this.requestTimestamps = this.requestTimestamps.filter(ts => ts > windowStart);
110
- // Calculate RPS based on window
111
- const requestsInWindow = this.requestTimestamps.length;
112
- return requestsInWindow / (this.RPS_WINDOW_SIZE / 1000);
56
+ };
57
+ // Throughput metrics implementation
58
+ this.throughput = {
59
+ instant: () => {
60
+ return this.throughputTracker.getRate(1);
61
+ },
62
+ recent: () => {
63
+ return this.throughputTracker.getRate(10);
64
+ },
65
+ average: () => {
66
+ return this.throughputTracker.getRate(60);
67
+ },
68
+ custom: (seconds) => {
69
+ return this.throughputTracker.getRate(seconds);
70
+ },
71
+ history: (seconds) => {
72
+ return this.throughputTracker.getHistory(seconds);
73
+ },
74
+ byRoute: (windowSeconds = 60) => {
75
+ const routeThroughput = new Map();
76
+ const now = Date.now();
77
+ const windowStart = now - (windowSeconds * 1000);
78
+ // Aggregate bytes by route from trackers
79
+ const routeBytes = new Map();
80
+ for (const [_, tracker] of this.connectionByteTrackers) {
81
+ if (tracker.lastUpdate > windowStart) {
82
+ const current = routeBytes.get(tracker.routeName) || { in: 0, out: 0 };
83
+ current.in += tracker.bytesIn;
84
+ current.out += tracker.bytesOut;
85
+ routeBytes.set(tracker.routeName, current);
86
+ }
87
+ }
88
+ // Convert to rates
89
+ for (const [route, bytes] of routeBytes) {
90
+ routeThroughput.set(route, {
91
+ in: Math.round(bytes.in / windowSeconds),
92
+ out: Math.round(bytes.out / windowSeconds)
93
+ });
94
+ }
95
+ return routeThroughput;
96
+ },
97
+ byIP: (windowSeconds = 60) => {
98
+ const ipThroughput = new Map();
99
+ const now = Date.now();
100
+ const windowStart = now - (windowSeconds * 1000);
101
+ // Aggregate bytes by IP from trackers
102
+ const ipBytes = new Map();
103
+ for (const [_, tracker] of this.connectionByteTrackers) {
104
+ if (tracker.lastUpdate > windowStart) {
105
+ const current = ipBytes.get(tracker.remoteIP) || { in: 0, out: 0 };
106
+ current.in += tracker.bytesIn;
107
+ current.out += tracker.bytesOut;
108
+ ipBytes.set(tracker.remoteIP, current);
109
+ }
110
+ }
111
+ // Convert to rates
112
+ for (const [ip, bytes] of ipBytes) {
113
+ ipThroughput.set(ip, {
114
+ in: Math.round(bytes.in / windowSeconds),
115
+ out: Math.round(bytes.out / windowSeconds)
116
+ });
117
+ }
118
+ return ipThroughput;
119
+ }
120
+ };
121
+ // Request metrics implementation
122
+ this.requests = {
123
+ perSecond: () => {
124
+ const now = Date.now();
125
+ const oneSecondAgo = now - 1000;
126
+ // Clean old timestamps
127
+ this.requestTimestamps = this.requestTimestamps.filter(ts => ts > now - 60000);
128
+ // Count requests in last second
129
+ const recentRequests = this.requestTimestamps.filter(ts => ts > oneSecondAgo);
130
+ return recentRequests.length;
131
+ },
132
+ perMinute: () => {
133
+ const now = Date.now();
134
+ const oneMinuteAgo = now - 60000;
135
+ // Count requests in last minute
136
+ const recentRequests = this.requestTimestamps.filter(ts => ts > oneMinuteAgo);
137
+ return recentRequests.length;
138
+ },
139
+ total: () => {
140
+ return this.totalRequests;
141
+ }
142
+ };
143
+ // Totals implementation
144
+ this.totals = {
145
+ bytesIn: () => {
146
+ let total = 0;
147
+ // Sum from all active connections
148
+ for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
149
+ total += record.bytesReceived;
150
+ }
151
+ // TODO: Add historical data from terminated connections
152
+ return total;
153
+ },
154
+ bytesOut: () => {
155
+ let total = 0;
156
+ // Sum from all active connections
157
+ for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
158
+ total += record.bytesSent;
159
+ }
160
+ // TODO: Add historical data from terminated connections
161
+ return total;
162
+ },
163
+ connections: () => {
164
+ return this.connections.total();
165
+ }
166
+ };
167
+ // Percentiles implementation (placeholder for now)
168
+ this.percentiles = {
169
+ connectionDuration: () => {
170
+ // TODO: Implement percentile calculations
171
+ return { p50: 0, p95: 0, p99: 0 };
172
+ },
173
+ bytesTransferred: () => {
174
+ // TODO: Implement percentile calculations
175
+ return {
176
+ in: { p50: 0, p95: 0, p99: 0 },
177
+ out: { p50: 0, p95: 0, p99: 0 }
178
+ };
179
+ }
180
+ };
181
+ this.sampleIntervalMs = config?.sampleIntervalMs || 1000;
182
+ this.retentionSeconds = config?.retentionSeconds || 3600;
183
+ this.throughputTracker = new ThroughputTracker(this.retentionSeconds);
113
184
  }
114
185
  /**
115
- * Record a new request for RPS tracking
186
+ * Record a new request
116
187
  */
117
- recordRequest() {
188
+ recordRequest(connectionId, routeName, remoteIP) {
118
189
  const now = Date.now();
119
190
  this.requestTimestamps.push(now);
120
- // Prevent unbounded growth - clean up more aggressively
121
- if (this.requestTimestamps.length > this.MAX_TIMESTAMPS) {
122
- // Keep only timestamps within the window
123
- const cutoff = now - this.RPS_WINDOW_SIZE;
191
+ this.totalRequests++;
192
+ // Initialize byte tracker for this connection
193
+ this.connectionByteTrackers.set(connectionId, {
194
+ connectionId,
195
+ routeName,
196
+ remoteIP,
197
+ bytesIn: 0,
198
+ bytesOut: 0,
199
+ lastUpdate: now
200
+ });
201
+ // Cleanup old request timestamps (keep last minute only)
202
+ if (this.requestTimestamps.length > 1000) {
203
+ const cutoff = now - 60000;
124
204
  this.requestTimestamps = this.requestTimestamps.filter(ts => ts > cutoff);
125
205
  }
126
206
  }
127
207
  /**
128
- * Get total throughput (bytes transferred)
129
- */
130
- getThroughput() {
131
- let bytesIn = 0;
132
- let bytesOut = 0;
133
- // Sum bytes from all active connections
134
- for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
135
- bytesIn += record.bytesReceived;
136
- bytesOut += record.bytesSent;
137
- }
138
- return { bytesIn, bytesOut };
139
- }
140
- /**
141
- * Get throughput rate (bytes per second) for last minute
208
+ * Record bytes transferred for a connection
142
209
  */
143
- getThroughputRate() {
144
- const now = Date.now();
145
- let recentBytesIn = 0;
146
- let recentBytesOut = 0;
147
- // Calculate bytes transferred in last minute from active connections
148
- for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
149
- const connectionAge = now - record.incomingStartTime;
150
- if (connectionAge < 60000) { // Connection started within last minute
151
- recentBytesIn += record.bytesReceived;
152
- recentBytesOut += record.bytesSent;
153
- }
154
- else {
155
- // For older connections, estimate rate based on average
156
- const rate = connectionAge / 60000;
157
- recentBytesIn += record.bytesReceived / rate;
158
- recentBytesOut += record.bytesSent / rate;
159
- }
210
+ recordBytes(connectionId, bytesIn, bytesOut) {
211
+ // Update global throughput tracker
212
+ this.throughputTracker.recordBytes(bytesIn, bytesOut);
213
+ // Update connection-specific tracker
214
+ const tracker = this.connectionByteTrackers.get(connectionId);
215
+ if (tracker) {
216
+ tracker.bytesIn += bytesIn;
217
+ tracker.bytesOut += bytesOut;
218
+ tracker.lastUpdate = Date.now();
160
219
  }
161
- return {
162
- bytesInPerSec: Math.round(recentBytesIn / 60),
163
- bytesOutPerSec: Math.round(recentBytesOut / 60)
164
- };
165
220
  }
166
221
  /**
167
- * Get top IPs by connection count
222
+ * Clean up tracking for a closed connection
168
223
  */
169
- getTopIPs(limit = 10) {
170
- const ipCounts = this.getConnectionsByIP();
171
- const sorted = Array.from(ipCounts.entries())
172
- .sort((a, b) => b[1] - a[1])
173
- .slice(0, limit)
174
- .map(([ip, connections]) => ({ ip, connections }));
175
- return sorted;
224
+ removeConnection(connectionId) {
225
+ this.connectionByteTrackers.delete(connectionId);
176
226
  }
177
227
  /**
178
- * Check if an IP has reached the connection limit
179
- */
180
- isIPBlocked(ip, maxConnectionsPerIP) {
181
- const ipCounts = this.getConnectionsByIP();
182
- const currentConnections = ipCounts.get(ip) || 0;
183
- return currentConnections >= maxConnectionsPerIP;
184
- }
185
- /**
186
- * Clean up old request timestamps
187
- */
188
- cleanupOldRequests() {
189
- const cutoff = Date.now() - this.RPS_WINDOW_SIZE;
190
- this.requestTimestamps = this.requestTimestamps.filter(ts => ts > cutoff);
191
- }
192
- /**
193
- * Start the metrics collector and set up subscriptions
228
+ * Start the metrics collector
194
229
  */
195
230
  start() {
196
231
  if (!this.smartProxy.routeConnectionHandler) {
197
232
  throw new Error('MetricsCollector: RouteConnectionHandler not available');
198
233
  }
199
- // Subscribe to the newConnectionSubject from RouteConnectionHandler
234
+ // Start periodic sampling
235
+ this.samplingInterval = setInterval(() => {
236
+ this.throughputTracker.takeSample();
237
+ // Clean up old connection trackers (connections closed more than 5 minutes ago)
238
+ const cutoff = Date.now() - 300000;
239
+ for (const [id, tracker] of this.connectionByteTrackers) {
240
+ if (tracker.lastUpdate < cutoff) {
241
+ this.connectionByteTrackers.delete(id);
242
+ }
243
+ }
244
+ }, this.sampleIntervalMs);
245
+ // Subscribe to new connections
200
246
  this.connectionSubscription = this.smartProxy.routeConnectionHandler.newConnectionSubject.subscribe({
201
247
  next: (record) => {
202
- this.recordRequest();
203
- // Optional: Log connection details
248
+ const routeName = record.routeConfig?.name || 'unknown';
249
+ this.recordRequest(record.id, routeName, record.remoteIP);
204
250
  if (this.smartProxy.settings?.enableDetailedLogging) {
205
251
  logger.log('debug', `MetricsCollector: New connection recorded`, {
206
252
  connectionId: record.id,
207
253
  remoteIP: record.remoteIP,
208
- routeName: record.routeConfig?.name || 'unknown',
254
+ routeName,
209
255
  component: 'metrics'
210
256
  });
211
257
  }
@@ -220,9 +266,13 @@ export class MetricsCollector {
220
266
  logger.log('debug', 'MetricsCollector started', { component: 'metrics' });
221
267
  }
222
268
  /**
223
- * Stop the metrics collector and clean up resources
269
+ * Stop the metrics collector
224
270
  */
225
271
  stop() {
272
+ if (this.samplingInterval) {
273
+ clearInterval(this.samplingInterval);
274
+ this.samplingInterval = undefined;
275
+ }
226
276
  if (this.connectionSubscription) {
227
277
  this.connectionSubscription.unsubscribe();
228
278
  this.connectionSubscription = undefined;
@@ -230,10 +280,10 @@ export class MetricsCollector {
230
280
  logger.log('debug', 'MetricsCollector stopped', { component: 'metrics' });
231
281
  }
232
282
  /**
233
- * Alias for stop() for backward compatibility
283
+ * Alias for stop() for compatibility
234
284
  */
235
285
  destroy() {
236
286
  this.stop();
237
287
  }
238
288
  }
239
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy1jb2xsZWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L21ldHJpY3MtY29sbGVjdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFHNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRXBEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGdCQUFnQjtJQWtCM0IsWUFDVSxVQUFzQjtRQUF0QixlQUFVLEdBQVYsVUFBVSxDQUFZO1FBbEJoQyxvREFBb0Q7UUFDNUMsc0JBQWlCLEdBQWEsRUFBRSxDQUFDO1FBQ3hCLG9CQUFlLEdBQUcsS0FBSyxDQUFDLENBQUMsa0JBQWtCO1FBQzNDLG1CQUFjLEdBQUcsSUFBSSxDQUFDLENBQUMsNkJBQTZCO1FBRXJFLG1DQUFtQztRQUMzQixrQkFBYSxHQUlqQixFQUFFLFNBQVMsRUFBRSxDQUFDLEVBQUUsQ0FBQztRQUVKLGNBQVMsR0FBRyxJQUFJLENBQUMsQ0FBQyxpQkFBaUI7UUFRbEQsZ0RBQWdEO0lBQ2xELENBQUM7SUFFRDs7T0FFRztJQUNJLG9CQUFvQjtRQUN6QixPQUFPLElBQUksQ0FBQyxVQUFVLENBQUMsaUJBQWlCLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztJQUNoRSxDQUFDO0lBRUQ7O09BRUc7SUFDSSxxQkFBcUI7UUFDMUIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRXZCLCtCQUErQjtRQUMvQixJQUFJLElBQUksQ0FBQyxhQUFhLENBQUMsa0JBQWtCO1lBQ3JDLEdBQUcsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDeEQsT0FBTyxJQUFJLEdBQUcsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLGtCQUFrQixDQUFDLENBQUM7UUFD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
289
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy1jb2xsZWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L21ldHJpY3MtY29sbGVjdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFRNUMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRXBEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGdCQUFnQjtJQW1CM0IsWUFDVSxVQUFzQixFQUM5QixNQUdDO1FBSk8sZUFBVSxHQUFWLFVBQVUsQ0FBWTtRQWhCaEMsbUJBQW1CO1FBQ1gsc0JBQWlCLEdBQWEsRUFBRSxDQUFDO1FBQ2pDLGtCQUFhLEdBQVcsQ0FBQyxDQUFDO1FBRWxDLG9EQUFvRDtRQUM1QywyQkFBc0IsR0FBRyxJQUFJLEdBQUcsRUFBd0IsQ0FBQztRQXNCakUsb0NBQW9DO1FBQzdCLGdCQUFXLEdBQUc7WUFDbkIsTUFBTSxFQUFFLEdBQVcsRUFBRTtnQkFDbkIsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDLGlCQUFpQixDQUFDLGtCQUFrQixFQUFFLENBQUM7WUFDaEUsQ0FBQztZQUVELEtBQUssRUFBRSxHQUFXLEVBQUU7Z0JBQ2xCLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsaUJBQWlCLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztnQkFDdEUsSUFBSSxLQUFLLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxpQkFBaUIsQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO2dCQUVuRSxLQUFLLE1BQU0sTUFBTSxJQUFJLEtBQUssQ0FBQyxRQUFRLEVBQUUsQ0FBQztvQkFDcEMsS0FBSyxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7Z0JBQ2xDLENBQUM7Z0JBRUQs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
@@ -76,6 +76,11 @@ export interface ISmartProxyOptions {
76
76
  extendedKeepAliveLifetime?: number;
77
77
  useHttpProxy?: number[];
78
78
  httpProxyPort?: number;
79
+ metrics?: {
80
+ enabled?: boolean;
81
+ sampleIntervalMs?: number;
82
+ retentionSeconds?: number;
83
+ };
79
84
  /**
80
85
  * Global ACME configuration options for SmartProxy
81
86
  *