@push.rocks/smartproxy 19.5.4 → 19.5.6
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/dist_ts/core/utils/async-utils.d.ts +81 -0
- package/dist_ts/core/utils/async-utils.js +216 -0
- package/dist_ts/core/utils/binary-heap.d.ts +73 -0
- package/dist_ts/core/utils/binary-heap.js +193 -0
- package/dist_ts/core/utils/enhanced-connection-pool.d.ts +110 -0
- package/dist_ts/core/utils/enhanced-connection-pool.js +320 -0
- package/dist_ts/core/utils/fs-utils.d.ts +144 -0
- package/dist_ts/core/utils/fs-utils.js +252 -0
- package/dist_ts/core/utils/index.d.ts +6 -2
- package/dist_ts/core/utils/index.js +7 -3
- package/dist_ts/core/utils/lifecycle-component.d.ts +59 -0
- package/dist_ts/core/utils/lifecycle-component.js +195 -0
- package/dist_ts/core/utils/socket-utils.d.ts +28 -0
- package/dist_ts/core/utils/socket-utils.js +77 -0
- package/dist_ts/forwarding/handlers/http-handler.js +7 -4
- package/dist_ts/forwarding/handlers/https-passthrough-handler.js +14 -55
- package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +52 -40
- package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +31 -43
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts/proxies/http-proxy/certificate-manager.d.ts +15 -0
- package/dist_ts/proxies/http-proxy/certificate-manager.js +49 -2
- package/dist_ts/proxies/http-proxy/connection-pool.js +4 -19
- package/dist_ts/proxies/http-proxy/http-proxy.js +3 -7
- package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +10 -0
- package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +53 -43
- package/dist_ts/proxies/smart-proxy/cert-store.js +22 -20
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +35 -9
- package/dist_ts/proxies/smart-proxy/connection-manager.js +243 -189
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +13 -2
- package/dist_ts/proxies/smart-proxy/port-manager.js +3 -3
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +35 -4
- package/package.json +2 -2
- package/readme.hints.md +96 -1
- package/readme.plan.md +1135 -221
- package/readme.problems.md +167 -83
- package/ts/core/utils/async-utils.ts +275 -0
- package/ts/core/utils/binary-heap.ts +225 -0
- package/ts/core/utils/enhanced-connection-pool.ts +420 -0
- package/ts/core/utils/fs-utils.ts +270 -0
- package/ts/core/utils/index.ts +6 -2
- package/ts/core/utils/lifecycle-component.ts +231 -0
- package/ts/core/utils/socket-utils.ts +96 -0
- package/ts/forwarding/handlers/http-handler.ts +7 -3
- package/ts/forwarding/handlers/https-passthrough-handler.ts +13 -62
- package/ts/forwarding/handlers/https-terminate-to-http-handler.ts +58 -46
- package/ts/forwarding/handlers/https-terminate-to-https-handler.ts +38 -53
- package/ts/plugins.ts +2 -1
- package/ts/proxies/http-proxy/certificate-manager.ts +52 -1
- package/ts/proxies/http-proxy/connection-pool.ts +3 -16
- package/ts/proxies/http-proxy/http-proxy.ts +2 -5
- package/ts/proxies/nftables-proxy/nftables-proxy.ts +64 -79
- package/ts/proxies/smart-proxy/cert-store.ts +26 -20
- package/ts/proxies/smart-proxy/connection-manager.ts +277 -197
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +15 -1
- package/ts/proxies/smart-proxy/port-manager.ts +2 -2
- package/ts/proxies/smart-proxy/route-connection-handler.ts +39 -4
- package/readme.plan2.md +0 -764
- package/ts/common/eventUtils.ts +0 -34
- package/ts/common/types.ts +0 -91
- package/ts/core/utils/event-system.ts +0 -376
- package/ts/core/utils/event-utils.ts +0 -25
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { LifecycleComponent } from './lifecycle-component.js';
|
|
2
|
+
import { BinaryHeap } from './binary-heap.js';
|
|
3
|
+
import { AsyncMutex } from './async-utils.js';
|
|
4
|
+
import { EventEmitter } from 'events';
|
|
5
|
+
/**
|
|
6
|
+
* Enhanced connection pool with priority queue, backpressure, and lifecycle management
|
|
7
|
+
*/
|
|
8
|
+
export class EnhancedConnectionPool extends LifecycleComponent {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
super();
|
|
11
|
+
this.availableConnections = [];
|
|
12
|
+
this.activeConnections = new Map();
|
|
13
|
+
this.mutex = new AsyncMutex();
|
|
14
|
+
this.eventEmitter = new EventEmitter();
|
|
15
|
+
this.connectionIdCounter = 0;
|
|
16
|
+
this.requestIdCounter = 0;
|
|
17
|
+
this.isClosing = false;
|
|
18
|
+
// Metrics
|
|
19
|
+
this.metrics = {
|
|
20
|
+
connectionsCreated: 0,
|
|
21
|
+
connectionsDestroyed: 0,
|
|
22
|
+
connectionsAcquired: 0,
|
|
23
|
+
connectionsReleased: 0,
|
|
24
|
+
acquireTimeouts: 0,
|
|
25
|
+
validationFailures: 0,
|
|
26
|
+
queueHighWaterMark: 0,
|
|
27
|
+
};
|
|
28
|
+
this.options = {
|
|
29
|
+
minSize: 0,
|
|
30
|
+
maxSize: 10,
|
|
31
|
+
acquireTimeout: 30000,
|
|
32
|
+
idleTimeout: 300000, // 5 minutes
|
|
33
|
+
maxUseCount: Infinity,
|
|
34
|
+
validateOnAcquire: true,
|
|
35
|
+
validateOnReturn: false,
|
|
36
|
+
queueTimeout: 60000,
|
|
37
|
+
...options,
|
|
38
|
+
};
|
|
39
|
+
// Initialize priority queue (higher priority = extracted first)
|
|
40
|
+
this.waitQueue = new BinaryHeap((a, b) => b.priority - a.priority || a.timestamp - b.timestamp, (item) => item.id);
|
|
41
|
+
// Start maintenance cycle
|
|
42
|
+
this.startMaintenance();
|
|
43
|
+
// Initialize minimum connections
|
|
44
|
+
this.initializeMinConnections();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Initialize minimum number of connections
|
|
48
|
+
*/
|
|
49
|
+
async initializeMinConnections() {
|
|
50
|
+
const promises = [];
|
|
51
|
+
for (let i = 0; i < this.options.minSize; i++) {
|
|
52
|
+
promises.push(this.createConnection()
|
|
53
|
+
.then(conn => {
|
|
54
|
+
this.availableConnections.push(conn);
|
|
55
|
+
})
|
|
56
|
+
.catch(err => {
|
|
57
|
+
if (this.options.onConnectionError) {
|
|
58
|
+
this.options.onConnectionError(err);
|
|
59
|
+
}
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
await Promise.all(promises);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Start maintenance timer for idle connection cleanup
|
|
66
|
+
*/
|
|
67
|
+
startMaintenance() {
|
|
68
|
+
this.setInterval(() => {
|
|
69
|
+
this.performMaintenance();
|
|
70
|
+
}, 30000); // Every 30 seconds
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Perform maintenance tasks
|
|
74
|
+
*/
|
|
75
|
+
async performMaintenance() {
|
|
76
|
+
await this.mutex.runExclusive(async () => {
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
const toRemove = [];
|
|
79
|
+
// Check for idle connections beyond minimum size
|
|
80
|
+
for (let i = this.availableConnections.length - 1; i >= 0; i--) {
|
|
81
|
+
const conn = this.availableConnections[i];
|
|
82
|
+
// Keep minimum connections
|
|
83
|
+
if (this.availableConnections.length <= this.options.minSize) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
// Remove idle connections
|
|
87
|
+
if (now - conn.lastUsedAt > this.options.idleTimeout) {
|
|
88
|
+
toRemove.push(conn);
|
|
89
|
+
this.availableConnections.splice(i, 1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Destroy idle connections
|
|
93
|
+
for (const conn of toRemove) {
|
|
94
|
+
await this.destroyConnection(conn);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Acquire a connection from the pool
|
|
100
|
+
*/
|
|
101
|
+
async acquire(priority = 0, timeout) {
|
|
102
|
+
if (this.isClosing) {
|
|
103
|
+
throw new Error('Connection pool is closing');
|
|
104
|
+
}
|
|
105
|
+
return this.mutex.runExclusive(async () => {
|
|
106
|
+
// Try to get an available connection
|
|
107
|
+
const connection = await this.tryAcquireConnection();
|
|
108
|
+
if (connection) {
|
|
109
|
+
return connection;
|
|
110
|
+
}
|
|
111
|
+
// Check if we can create a new connection
|
|
112
|
+
const totalConnections = this.availableConnections.length + this.activeConnections.size;
|
|
113
|
+
if (totalConnections < this.options.maxSize) {
|
|
114
|
+
try {
|
|
115
|
+
const newConnection = await this.createConnection();
|
|
116
|
+
return this.checkoutConnection(newConnection);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
// Fall through to queue if creation fails
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Add to wait queue
|
|
123
|
+
return this.queueAcquireRequest(priority, timeout);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Try to acquire an available connection
|
|
128
|
+
*/
|
|
129
|
+
async tryAcquireConnection() {
|
|
130
|
+
while (this.availableConnections.length > 0) {
|
|
131
|
+
const connection = this.availableConnections.shift();
|
|
132
|
+
// Check if connection exceeded max use count
|
|
133
|
+
if (connection.useCount >= this.options.maxUseCount) {
|
|
134
|
+
await this.destroyConnection(connection);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
// Validate connection if required
|
|
138
|
+
if (this.options.validateOnAcquire && this.options.connectionValidator) {
|
|
139
|
+
try {
|
|
140
|
+
const isValid = await this.options.connectionValidator(connection.connection);
|
|
141
|
+
if (!isValid) {
|
|
142
|
+
this.metrics.validationFailures++;
|
|
143
|
+
await this.destroyConnection(connection);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
this.metrics.validationFailures++;
|
|
149
|
+
await this.destroyConnection(connection);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return this.checkoutConnection(connection);
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Checkout a connection for use
|
|
159
|
+
*/
|
|
160
|
+
checkoutConnection(connection) {
|
|
161
|
+
connection.inUse = true;
|
|
162
|
+
connection.lastUsedAt = Date.now();
|
|
163
|
+
connection.useCount++;
|
|
164
|
+
this.activeConnections.set(connection.id, connection);
|
|
165
|
+
this.metrics.connectionsAcquired++;
|
|
166
|
+
this.eventEmitter.emit('acquire', connection);
|
|
167
|
+
return connection;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Queue an acquire request
|
|
171
|
+
*/
|
|
172
|
+
queueAcquireRequest(priority, timeout) {
|
|
173
|
+
return new Promise((resolve, reject) => {
|
|
174
|
+
const request = {
|
|
175
|
+
id: `req-${this.requestIdCounter++}`,
|
|
176
|
+
priority,
|
|
177
|
+
timestamp: Date.now(),
|
|
178
|
+
resolve,
|
|
179
|
+
reject,
|
|
180
|
+
};
|
|
181
|
+
// Set timeout
|
|
182
|
+
const timeoutMs = timeout || this.options.queueTimeout;
|
|
183
|
+
request.timeoutHandle = this.setTimeout(() => {
|
|
184
|
+
if (this.waitQueue.extractByKey(request.id)) {
|
|
185
|
+
this.metrics.acquireTimeouts++;
|
|
186
|
+
reject(new Error(`Connection acquire timeout after ${timeoutMs}ms`));
|
|
187
|
+
}
|
|
188
|
+
}, timeoutMs);
|
|
189
|
+
this.waitQueue.insert(request);
|
|
190
|
+
this.metrics.queueHighWaterMark = Math.max(this.metrics.queueHighWaterMark, this.waitQueue.size);
|
|
191
|
+
this.eventEmitter.emit('enqueue', { queueSize: this.waitQueue.size });
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Release a connection back to the pool
|
|
196
|
+
*/
|
|
197
|
+
async release(connection) {
|
|
198
|
+
return this.mutex.runExclusive(async () => {
|
|
199
|
+
if (!connection.inUse || !this.activeConnections.has(connection.id)) {
|
|
200
|
+
throw new Error('Connection is not active');
|
|
201
|
+
}
|
|
202
|
+
this.activeConnections.delete(connection.id);
|
|
203
|
+
connection.inUse = false;
|
|
204
|
+
connection.lastUsedAt = Date.now();
|
|
205
|
+
this.metrics.connectionsReleased++;
|
|
206
|
+
// Check if connection should be destroyed
|
|
207
|
+
if (connection.useCount >= this.options.maxUseCount) {
|
|
208
|
+
await this.destroyConnection(connection);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// Validate on return if required
|
|
212
|
+
if (this.options.validateOnReturn && this.options.connectionValidator) {
|
|
213
|
+
try {
|
|
214
|
+
const isValid = await this.options.connectionValidator(connection.connection);
|
|
215
|
+
if (!isValid) {
|
|
216
|
+
await this.destroyConnection(connection);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
await this.destroyConnection(connection);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Check if there are waiting requests
|
|
226
|
+
const request = this.waitQueue.extract();
|
|
227
|
+
if (request) {
|
|
228
|
+
this.clearTimeout(request.timeoutHandle);
|
|
229
|
+
request.resolve(this.checkoutConnection(connection));
|
|
230
|
+
this.eventEmitter.emit('dequeue', { queueSize: this.waitQueue.size });
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
// Return to available pool
|
|
234
|
+
this.availableConnections.push(connection);
|
|
235
|
+
this.eventEmitter.emit('release', connection);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Create a new connection
|
|
241
|
+
*/
|
|
242
|
+
async createConnection() {
|
|
243
|
+
const rawConnection = await this.options.connectionFactory();
|
|
244
|
+
const connection = {
|
|
245
|
+
id: `conn-${this.connectionIdCounter++}`,
|
|
246
|
+
connection: rawConnection,
|
|
247
|
+
createdAt: Date.now(),
|
|
248
|
+
lastUsedAt: Date.now(),
|
|
249
|
+
useCount: 0,
|
|
250
|
+
inUse: false,
|
|
251
|
+
};
|
|
252
|
+
this.metrics.connectionsCreated++;
|
|
253
|
+
this.eventEmitter.emit('create', connection);
|
|
254
|
+
return connection;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Destroy a connection
|
|
258
|
+
*/
|
|
259
|
+
async destroyConnection(connection) {
|
|
260
|
+
try {
|
|
261
|
+
if (this.options.connectionDestroyer) {
|
|
262
|
+
await this.options.connectionDestroyer(connection.connection);
|
|
263
|
+
}
|
|
264
|
+
this.metrics.connectionsDestroyed++;
|
|
265
|
+
this.eventEmitter.emit('destroy', connection);
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
if (this.options.onConnectionError) {
|
|
269
|
+
this.options.onConnectionError(err, connection.connection);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Get current pool statistics
|
|
275
|
+
*/
|
|
276
|
+
getStats() {
|
|
277
|
+
return {
|
|
278
|
+
available: this.availableConnections.length,
|
|
279
|
+
active: this.activeConnections.size,
|
|
280
|
+
waiting: this.waitQueue.size,
|
|
281
|
+
total: this.availableConnections.length + this.activeConnections.size,
|
|
282
|
+
...this.metrics,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Subscribe to pool events
|
|
287
|
+
*/
|
|
288
|
+
on(event, listener) {
|
|
289
|
+
this.addEventListener(this.eventEmitter, event, listener);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Close the pool and cleanup resources
|
|
293
|
+
*/
|
|
294
|
+
async onCleanup() {
|
|
295
|
+
this.isClosing = true;
|
|
296
|
+
// Clear the wait queue
|
|
297
|
+
while (!this.waitQueue.isEmpty()) {
|
|
298
|
+
const request = this.waitQueue.extract();
|
|
299
|
+
if (request) {
|
|
300
|
+
this.clearTimeout(request.timeoutHandle);
|
|
301
|
+
request.reject(new Error('Connection pool is closing'));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Wait for active connections to be released (with timeout)
|
|
305
|
+
const timeout = 30000;
|
|
306
|
+
const startTime = Date.now();
|
|
307
|
+
while (this.activeConnections.size > 0 && Date.now() - startTime < timeout) {
|
|
308
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
309
|
+
}
|
|
310
|
+
// Destroy all connections
|
|
311
|
+
const allConnections = [
|
|
312
|
+
...this.availableConnections,
|
|
313
|
+
...this.activeConnections.values(),
|
|
314
|
+
];
|
|
315
|
+
await Promise.all(allConnections.map(conn => this.destroyConnection(conn)));
|
|
316
|
+
this.availableConnections.length = 0;
|
|
317
|
+
this.activeConnections.clear();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW5oYW5jZWQtY29ubmVjdGlvbi1wb29sLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvY29yZS91dGlscy9lbmhhbmNlZC1jb25uZWN0aW9uLXBvb2wudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDOUQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBNkN0Qzs7R0FFRztBQUNILE1BQU0sT0FBTyxzQkFBMEIsU0FBUSxrQkFBa0I7SUF1Qi9ELFlBQVksT0FBa0M7UUFDNUMsS0FBSyxFQUFFLENBQUM7UUF0Qk8seUJBQW9CLEdBQTJCLEVBQUUsQ0FBQztRQUNsRCxzQkFBaUIsR0FBc0MsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUVqRSxVQUFLLEdBQUcsSUFBSSxVQUFVLEVBQUUsQ0FBQztRQUN6QixpQkFBWSxHQUFHLElBQUksWUFBWSxFQUFFLENBQUM7UUFFM0Msd0JBQW1CLEdBQUcsQ0FBQyxDQUFDO1FBQ3hCLHFCQUFnQixHQUFHLENBQUMsQ0FBQztRQUNyQixjQUFTLEdBQUcsS0FBSyxDQUFDO1FBRTFCLFVBQVU7UUFDRixZQUFPLEdBQUc7WUFDaEIsa0JBQWtCLEVBQUUsQ0FBQztZQUNyQixvQkFBb0IsRUFBRSxDQUFDO1lBQ3ZCLG1CQUFtQixFQUFFLENBQUM7WUFDdEIsbUJBQW1CLEVBQUUsQ0FBQztZQUN0QixlQUFlLEVBQUUsQ0FBQztZQUNsQixrQkFBa0IsRUFBRSxDQUFDO1lBQ3JCLGtCQUFrQixFQUFFLENBQUM7U0FDdEIsQ0FBQztRQUtBLElBQUksQ0FBQyxPQUFPLEdBQUc7WUFDYixPQUFPLEVBQUUsQ0FBQztZQUNWLE9BQU8sRUFBRSxFQUFFO1lBQ1gsY0FBYyxFQUFFLEtBQUs7WUFDckIsV0FBVyxFQUFFLE1BQU0sRUFBRSxZQUFZO1lBQ2pDLFdBQVcsRUFBRSxRQUFRO1lBQ3JCLGlCQUFpQixFQUFFLElBQUk7WUFDdkIsZ0JBQWdCLEVBQUUsS0FBSztZQUN2QixZQUFZLEVBQUUsS0FBSztZQUNuQixHQUFHLE9BQU87U0FDWCxDQUFDO1FBRUYsZ0VBQWdFO1FBQ2hFLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxVQUFVLENBQzdCLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsR0FBRyxDQUFDLENBQUMsUUFBUSxJQUFJLENBQUMsQ0FBQyxTQUFTLEdBQUcsQ0FBQyxDQUFDLFNBQVMsRUFDOUQsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQ2xCLENBQUM7UUFFRiwwQkFBMEI7UUFDMUIsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7UUFFeEIsaUNBQWlDO1FBQ2pDLElBQUksQ0FBQyx3QkFBd0IsRUFBRSxDQUFDO0lBQ2xDLENBQUM7SUFFRDs7T0FFRztJQUNLLEtBQUssQ0FBQyx3QkFBd0I7UUFDcEMsTUFBTSxRQUFRLEdBQW9CLEVBQUUsQ0FBQztRQUVyQyxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUM5QyxRQUFRLENBQUMsSUFBSSxDQUNYLElBQUksQ0FBQyxnQkFBZ0IsRUFBRTtpQkFDcEIsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFO2dCQUNYLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDdkMsQ0FBQyxDQUFDO2lCQUNELEtBQUssQ0FBQyxHQUFHLENBQUMsRUFBRTtnQkFDWCxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztvQkFDbkMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDdEMsQ0FBQztZQUNILENBQUMsQ0FBQyxDQUNMLENBQUM7UUFDSixDQUFDO1FBRUQsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQzlCLENBQUM7SUFFRDs7T0FFRztJQUNLLGdCQUFnQjtRQUN0QixJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsRUFBRTtZQUNwQixJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztRQUM1QixDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQyxtQkFBbUI7SUFDaEMsQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLGtCQUFrQjtRQUM5QixNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLEtBQUssSUFBSSxFQUFFO1lBQ3ZDLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUN2QixNQUFNLFFBQVEsR0FBMkIsRUFBRSxDQUFDO1lBRTVDLGlEQUFpRDtZQUNqRCxLQUFLLElBQUksQ0FBQyxHQUFHLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztnQkFDL0QsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLG9CQUFvQixDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUUxQywyQkFBMkI7Z0JBQzNCLElBQUksSUFBSSxDQUFDLG9CQUFvQixDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO29CQUM3RCxNQUFNO2dCQUNSLENBQUM7Z0JBRUQsMEJBQTBCO2dCQUMxQixJQUFJLEdBQUcsR0FBRyxJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUM7b0JBQ3JELFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7b0JBQ3BCLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO2dCQUN6QyxDQUFDO1lBQ0gsQ0FBQztZQUVELDJCQUEyQjtZQUMzQixLQUFLLE1BQU0sSUFBSSxJQUFJLFFBQVEsRUFBRSxDQUFDO2dCQUM1QixNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUNyQyxDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsT0FBTyxDQUFDLFdBQW1CLENBQUMsRUFBRSxPQUFnQjtRQUN6RCxJQUFJLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNuQixNQUFNLElBQUksS0FBSyxDQUFDLDRCQUE0QixDQUFDLENBQUM7UUFDaEQsQ0FBQztRQUVELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQUMsS0FBSyxJQUFJLEVBQUU7WUFDeEMscUNBQXFDO1lBQ3JDLE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLG9CQUFvQixFQUFFLENBQUM7WUFDckQsSUFBSSxVQUFVLEVBQUUsQ0FBQztnQkFDZixPQUFPLFVBQVUsQ0FBQztZQUNwQixDQUFDO1lBRUQsMENBQTBDO1lBQzFDLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLG9CQUFvQixDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDO1lBQ3hGLElBQUksZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDNUMsSUFBSSxDQUFDO29CQUNILE1BQU0sYUFBYSxHQUFHLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7b0JBQ3BELE9BQU8sSUFBSSxDQUFDLGtCQUFrQixDQUFDLGFBQWEsQ0FBQyxDQUFDO2dCQUNoRCxDQUFDO2dCQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7b0JBQ2IsMENBQTBDO2dCQUM1QyxDQUFDO1lBQ0gsQ0FBQztZQUVELG9CQUFvQjtZQUNwQixPQUFPLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxRQUFRLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDckQsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxLQUFLLENBQUMsb0JBQW9CO1FBQ2hDLE9BQU8sSUFBSSxDQUFDLG9CQUFvQixDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUM1QyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsb0JBQW9CLENBQUMsS0FBSyxFQUFHLENBQUM7WUFFdEQsNkNBQTZDO1lBQzdDLElBQUksVUFBVSxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsRUFBRSxDQUFDO2dCQUNwRCxNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxVQUFVLENBQUMsQ0FBQztnQkFDekMsU0FBUztZQUNYLENBQUM7WUFFRCxrQ0FBa0M7WUFDbEMsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLGlCQUFpQixJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztnQkFDdkUsSUFBSSxDQUFDO29CQUNILE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUM7b0JBQzlFLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQzt3QkFDYixJQUFJLENBQUMsT0FBTyxDQUFDLGtCQUFrQixFQUFFLENBQUM7d0JBQ2xDLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixDQUFDLFVBQVUsQ0FBQyxDQUFDO3dCQUN6QyxTQUFTO29CQUNYLENBQUM7Z0JBQ0gsQ0FBQztnQkFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO29CQUNiLElBQUksQ0FBQyxPQUFPLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztvQkFDbEMsTUFBTSxJQUFJLENBQUMsaUJBQWlCLENBQUMsVUFBVSxDQUFDLENBQUM7b0JBQ3pDLFNBQVM7Z0JBQ1gsQ0FBQztZQUNILENBQUM7WUFFRCxPQUFPLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUM3QyxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxrQkFBa0IsQ0FBQyxVQUFnQztRQUN6RCxVQUFVLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQztRQUN4QixVQUFVLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUNuQyxVQUFVLENBQUMsUUFBUSxFQUFFLENBQUM7UUFFdEIsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsRUFBRSxFQUFFLFVBQVUsQ0FBQyxDQUFDO1FBQ3RELElBQUksQ0FBQyxPQUFPLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUVuQyxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDOUMsT0FBTyxVQUFVLENBQUM7SUFDcEIsQ0FBQztJQUVEOztPQUVHO0lBQ0ssbUJBQW1CLENBQUMsUUFBZ0IsRUFBRSxPQUFnQjtRQUM1RCxPQUFPLElBQUksT0FBTyxDQUF1QixDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtZQUMzRCxNQUFNLE9BQU8sR0FBdUI7Z0JBQ2xDLEVBQUUsRUFBRSxPQUFPLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxFQUFFO2dCQUNwQyxRQUFRO2dCQUNSLFNBQVMsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFO2dCQUNyQixPQUFPO2dCQUNQLE1BQU07YUFDUCxDQUFDO1lBRUYsY0FBYztZQUNkLE1BQU0sU0FBUyxHQUFHLE9BQU8sSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQztZQUN2RCxPQUFPLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxFQUFFO2dCQUMzQyxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDO29CQUM1QyxJQUFJLENBQUMsT0FBTyxDQUFDLGVBQWUsRUFBRSxDQUFDO29CQUMvQixNQUFNLENBQUMsSUFBSSxLQUFLLENBQUMsb0NBQW9DLFNBQVMsSUFBSSxDQUFDLENBQUMsQ0FBQztnQkFDdkUsQ0FBQztZQUNILENBQUMsRUFBRSxTQUFTLENBQUMsQ0FBQztZQUVkLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQy9CLElBQUksQ0FBQyxPQUFPLENBQUMsa0JBQWtCLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FDeEMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsRUFDL0IsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQ3BCLENBQUM7WUFFRixJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ3hFLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFnQztRQUNuRCxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLEtBQUssSUFBSSxFQUFFO1lBQ3hDLElBQUksQ0FBQyxVQUFVLENBQUMsS0FBSyxJQUFJLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQztnQkFDcEUsTUFBTSxJQUFJLEtBQUssQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO1lBQzlDLENBQUM7WUFFRCxJQUFJLENBQUMsaUJBQWlCLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUM3QyxVQUFVLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztZQUN6QixVQUFVLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUNuQyxJQUFJLENBQUMsT0FBTyxDQUFDLG1CQUFtQixFQUFFLENBQUM7WUFFbkMsMENBQTBDO1lBQzFDLElBQUksVUFBVSxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsRUFBRSxDQUFDO2dCQUNwRCxNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxVQUFVLENBQUMsQ0FBQztnQkFDekMsT0FBTztZQUNULENBQUM7WUFFRCxpQ0FBaUM7WUFDakMsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLGdCQUFnQixJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztnQkFDdEUsSUFBSSxDQUFDO29CQUNILE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUM7b0JBQzlFLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQzt3QkFDYixNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxVQUFVLENBQUMsQ0FBQzt3QkFDekMsT0FBTztvQkFDVCxDQUFDO2dCQUNILENBQUM7Z0JBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztvQkFDYixNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxVQUFVLENBQUMsQ0FBQztvQkFDekMsT0FBTztnQkFDVCxDQUFDO1lBQ0gsQ0FBQztZQUVELHNDQUFzQztZQUN0QyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3pDLElBQUksT0FBTyxFQUFFLENBQUM7Z0JBQ1osSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsYUFBYyxDQUFDLENBQUM7Z0JBQzFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUM7Z0JBQ3JELElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7WUFDeEUsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLDJCQUEyQjtnQkFDM0IsSUFBSSxDQUFDLG9CQUFvQixDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztnQkFDM0MsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLFVBQVUsQ0FBQyxDQUFDO1lBQ2hELENBQUM7UUFDSCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNLLEtBQUssQ0FBQyxnQkFBZ0I7UUFDNUIsTUFBTSxhQUFhLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFFN0QsTUFBTSxVQUFVLEdBQXlCO1lBQ3ZDLEVBQUUsRUFBRSxRQUFRLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxFQUFFO1lBQ3hDLFVBQVUsRUFBRSxhQUFhO1lBQ3pCLFNBQVMsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFO1lBQ3JCLFVBQVUsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFO1lBQ3RCLFFBQVEsRUFBRSxDQUFDO1lBQ1gsS0FBSyxFQUFFLEtBQUs7U0FDYixDQUFDO1FBRUYsSUFBSSxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1FBQ2xDLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUU3QyxPQUFPLFVBQVUsQ0FBQztJQUNwQixDQUFDO0lBRUQ7O09BRUc7SUFDSyxLQUFLLENBQUMsaUJBQWlCLENBQUMsVUFBZ0M7UUFDOUQsSUFBSSxDQUFDO1lBQ0gsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLG1CQUFtQixFQUFFLENBQUM7Z0JBQ3JDLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUM7WUFDaEUsQ0FBQztZQUVELElBQUksQ0FBQyxPQUFPLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztZQUNwQyxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDaEQsQ0FBQztRQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7WUFDYixJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztnQkFDbkMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxHQUFZLEVBQUUsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQ3RFLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ksUUFBUTtRQUNiLE9BQU87WUFDTCxTQUFTLEVBQUUsSUFBSSxDQUFDLG9CQUFvQixDQUFDLE1BQU07WUFDM0MsTUFBTSxFQUFFLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJO1lBQ25DLE9BQU8sRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUk7WUFDNUIsS0FBSyxFQUFFLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUk7WUFDckUsR0FBRyxJQUFJLENBQUMsT0FBTztTQUNoQixDQUFDO0lBQ0osQ0FBQztJQUVEOztPQUVHO0lBQ0ksRUFBRSxDQUFDLEtBQWEsRUFBRSxRQUFrQjtRQUN6QyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSxLQUFLLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDNUQsQ0FBQztJQUVEOztPQUVHO0lBQ08sS0FBSyxDQUFDLFNBQVM7UUFDdkIsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUM7UUFFdEIsdUJBQXVCO1FBQ3ZCLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUM7WUFDakMsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUN6QyxJQUFJLE9BQU8sRUFBRSxDQUFDO2dCQUNaLElBQUksQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDLGFBQWMsQ0FBQyxDQUFDO2dCQUMxQyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLDRCQUE0QixDQUFDLENBQUMsQ0FBQztZQUMxRCxDQUFDO1FBQ0gsQ0FBQztRQUVELDREQUE0RDtRQUM1RCxNQUFNLE9BQU8sR0FBRyxLQUFLLENBQUM7UUFDdEIsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRTdCLE9BQU8sSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksR0FBRyxDQUFDLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLFNBQVMsR0FBRyxPQUFPLEVBQUUsQ0FBQztZQUMzRSxNQUFNLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ3pELENBQUM7UUFFRCwwQkFBMEI7UUFDMUIsTUFBTSxjQUFjLEdBQUc7WUFDckIsR0FBRyxJQUFJLENBQUMsb0JBQW9CO1lBQzVCLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRTtTQUNuQyxDQUFDO1FBRUYsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBRTVFLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO1FBQ3JDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUNqQyxDQUFDO0NBQ0YifQ==
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async filesystem utilities for SmartProxy
|
|
3
|
+
* Provides non-blocking alternatives to synchronous filesystem operations
|
|
4
|
+
*/
|
|
5
|
+
import * as plugins from '../../plugins.js';
|
|
6
|
+
export declare class AsyncFileSystem {
|
|
7
|
+
/**
|
|
8
|
+
* Check if a file or directory exists
|
|
9
|
+
* @param path - Path to check
|
|
10
|
+
* @returns Promise resolving to true if exists, false otherwise
|
|
11
|
+
*/
|
|
12
|
+
static exists(path: string): Promise<boolean>;
|
|
13
|
+
/**
|
|
14
|
+
* Ensure a directory exists, creating it if necessary
|
|
15
|
+
* @param dirPath - Directory path to ensure
|
|
16
|
+
* @returns Promise that resolves when directory is ensured
|
|
17
|
+
*/
|
|
18
|
+
static ensureDir(dirPath: string): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Read a file as string
|
|
21
|
+
* @param filePath - Path to the file
|
|
22
|
+
* @param encoding - File encoding (default: utf8)
|
|
23
|
+
* @returns Promise resolving to file contents
|
|
24
|
+
*/
|
|
25
|
+
static readFile(filePath: string, encoding?: BufferEncoding): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
* Read a file as buffer
|
|
28
|
+
* @param filePath - Path to the file
|
|
29
|
+
* @returns Promise resolving to file buffer
|
|
30
|
+
*/
|
|
31
|
+
static readFileBuffer(filePath: string): Promise<Buffer>;
|
|
32
|
+
/**
|
|
33
|
+
* Write string data to a file
|
|
34
|
+
* @param filePath - Path to the file
|
|
35
|
+
* @param data - String data to write
|
|
36
|
+
* @param encoding - File encoding (default: utf8)
|
|
37
|
+
* @returns Promise that resolves when file is written
|
|
38
|
+
*/
|
|
39
|
+
static writeFile(filePath: string, data: string, encoding?: BufferEncoding): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Write buffer data to a file
|
|
42
|
+
* @param filePath - Path to the file
|
|
43
|
+
* @param data - Buffer data to write
|
|
44
|
+
* @returns Promise that resolves when file is written
|
|
45
|
+
*/
|
|
46
|
+
static writeFileBuffer(filePath: string, data: Buffer): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Remove a file
|
|
49
|
+
* @param filePath - Path to the file
|
|
50
|
+
* @returns Promise that resolves when file is removed
|
|
51
|
+
*/
|
|
52
|
+
static remove(filePath: string): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Remove a directory and all its contents
|
|
55
|
+
* @param dirPath - Path to the directory
|
|
56
|
+
* @returns Promise that resolves when directory is removed
|
|
57
|
+
*/
|
|
58
|
+
static removeDir(dirPath: string): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Read JSON from a file
|
|
61
|
+
* @param filePath - Path to the JSON file
|
|
62
|
+
* @returns Promise resolving to parsed JSON
|
|
63
|
+
*/
|
|
64
|
+
static readJSON<T = any>(filePath: string): Promise<T>;
|
|
65
|
+
/**
|
|
66
|
+
* Write JSON to a file
|
|
67
|
+
* @param filePath - Path to the file
|
|
68
|
+
* @param data - Data to write as JSON
|
|
69
|
+
* @param pretty - Whether to pretty-print JSON (default: true)
|
|
70
|
+
* @returns Promise that resolves when file is written
|
|
71
|
+
*/
|
|
72
|
+
static writeJSON(filePath: string, data: any, pretty?: boolean): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Copy a file from source to destination
|
|
75
|
+
* @param source - Source file path
|
|
76
|
+
* @param destination - Destination file path
|
|
77
|
+
* @returns Promise that resolves when file is copied
|
|
78
|
+
*/
|
|
79
|
+
static copyFile(source: string, destination: string): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Move/rename a file
|
|
82
|
+
* @param source - Source file path
|
|
83
|
+
* @param destination - Destination file path
|
|
84
|
+
* @returns Promise that resolves when file is moved
|
|
85
|
+
*/
|
|
86
|
+
static moveFile(source: string, destination: string): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Get file stats
|
|
89
|
+
* @param filePath - Path to the file
|
|
90
|
+
* @returns Promise resolving to file stats or null if doesn't exist
|
|
91
|
+
*/
|
|
92
|
+
static getStats(filePath: string): Promise<plugins.fs.Stats | null>;
|
|
93
|
+
/**
|
|
94
|
+
* List files in a directory
|
|
95
|
+
* @param dirPath - Directory path
|
|
96
|
+
* @returns Promise resolving to array of filenames
|
|
97
|
+
*/
|
|
98
|
+
static listFiles(dirPath: string): Promise<string[]>;
|
|
99
|
+
/**
|
|
100
|
+
* List files in a directory with full paths
|
|
101
|
+
* @param dirPath - Directory path
|
|
102
|
+
* @returns Promise resolving to array of full file paths
|
|
103
|
+
*/
|
|
104
|
+
static listFilesFullPath(dirPath: string): Promise<string[]>;
|
|
105
|
+
/**
|
|
106
|
+
* Recursively list all files in a directory
|
|
107
|
+
* @param dirPath - Directory path
|
|
108
|
+
* @param fileList - Accumulator for file list (used internally)
|
|
109
|
+
* @returns Promise resolving to array of all file paths
|
|
110
|
+
*/
|
|
111
|
+
static listFilesRecursive(dirPath: string, fileList?: string[]): Promise<string[]>;
|
|
112
|
+
/**
|
|
113
|
+
* Create a read stream for a file
|
|
114
|
+
* @param filePath - Path to the file
|
|
115
|
+
* @param options - Stream options
|
|
116
|
+
* @returns Read stream
|
|
117
|
+
*/
|
|
118
|
+
static createReadStream(filePath: string, options?: Parameters<typeof plugins.fs.createReadStream>[1]): plugins.fs.ReadStream;
|
|
119
|
+
/**
|
|
120
|
+
* Create a write stream for a file
|
|
121
|
+
* @param filePath - Path to the file
|
|
122
|
+
* @param options - Stream options
|
|
123
|
+
* @returns Write stream
|
|
124
|
+
*/
|
|
125
|
+
static createWriteStream(filePath: string, options?: Parameters<typeof plugins.fs.createWriteStream>[1]): plugins.fs.WriteStream;
|
|
126
|
+
/**
|
|
127
|
+
* Ensure a file exists, creating an empty file if necessary
|
|
128
|
+
* @param filePath - Path to the file
|
|
129
|
+
* @returns Promise that resolves when file is ensured
|
|
130
|
+
*/
|
|
131
|
+
static ensureFile(filePath: string): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Check if a path is a directory
|
|
134
|
+
* @param path - Path to check
|
|
135
|
+
* @returns Promise resolving to true if directory, false otherwise
|
|
136
|
+
*/
|
|
137
|
+
static isDirectory(path: string): Promise<boolean>;
|
|
138
|
+
/**
|
|
139
|
+
* Check if a path is a file
|
|
140
|
+
* @param path - Path to check
|
|
141
|
+
* @returns Promise resolving to true if file, false otherwise
|
|
142
|
+
*/
|
|
143
|
+
static isFile(path: string): Promise<boolean>;
|
|
144
|
+
}
|