@push.rocks/smartproxy 23.0.0 → 23.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/changelog.md +10 -0
- package/dist_rust/{rustproxy → rustproxy_linux_amd64} +0 -0
- package/dist_rust/rustproxy_linux_arm64 +0 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +9 -21
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +83 -212
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +2 -3
- package/npmextra.json +3 -0
- package/package.json +13 -11
- package/readme.md +41 -11
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/plugins.ts +2 -0
- package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +102 -233
- package/ts/proxies/smart-proxy/smart-proxy.ts +1 -2
- package/dist_ts/common/eventUtils.d.ts +0 -14
- package/dist_ts/common/eventUtils.js +0 -20
- package/dist_ts/common/types.d.ts +0 -82
- package/dist_ts/common/types.js +0 -15
- package/dist_ts/core/utils/event-system.d.ts +0 -200
- package/dist_ts/core/utils/event-system.js +0 -224
- package/dist_ts/core/utils/event-utils.d.ts +0 -15
- package/dist_ts/core/utils/event-utils.js +0 -11
- package/dist_ts/core/utils/route-manager.d.ts +0 -88
- package/dist_ts/core/utils/route-manager.js +0 -342
- package/dist_ts/core/utils/route-utils.d.ts +0 -28
- package/dist_ts/core/utils/route-utils.js +0 -67
- package/dist_ts/detection/detectors/http-detector-v2.d.ts +0 -33
- package/dist_ts/detection/detectors/http-detector-v2.js +0 -87
- package/dist_ts/detection/detectors/tls-detector-v2.d.ts +0 -33
- package/dist_ts/detection/detectors/tls-detector-v2.js +0 -80
- package/dist_ts/detection/protocol-detector-v2.d.ts +0 -46
- package/dist_ts/detection/protocol-detector-v2.js +0 -116
- package/dist_ts/forwarding/config/forwarding-types.d.ts +0 -42
- package/dist_ts/forwarding/config/forwarding-types.js +0 -18
- package/dist_ts/forwarding/config/index.d.ts +0 -9
- package/dist_ts/forwarding/config/index.js +0 -10
- package/dist_ts/forwarding/factory/forwarding-factory.d.ts +0 -25
- package/dist_ts/forwarding/factory/forwarding-factory.js +0 -172
- package/dist_ts/forwarding/factory/index.d.ts +0 -4
- package/dist_ts/forwarding/factory/index.js +0 -5
- package/dist_ts/forwarding/handlers/base-handler.d.ts +0 -62
- package/dist_ts/forwarding/handlers/base-handler.js +0 -121
- package/dist_ts/forwarding/handlers/http-handler.d.ts +0 -30
- package/dist_ts/forwarding/handlers/http-handler.js +0 -143
- package/dist_ts/forwarding/handlers/https-passthrough-handler.d.ts +0 -29
- package/dist_ts/forwarding/handlers/https-passthrough-handler.js +0 -156
- package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.d.ts +0 -36
- package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +0 -276
- package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.d.ts +0 -35
- package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +0 -261
- package/dist_ts/forwarding/handlers/index.d.ts +0 -8
- package/dist_ts/forwarding/handlers/index.js +0 -9
- package/dist_ts/forwarding/index.d.ts +0 -13
- package/dist_ts/forwarding/index.js +0 -16
- package/dist_ts/http/index.d.ts +0 -5
- package/dist_ts/http/index.js +0 -8
- package/dist_ts/http/models/http-types.d.ts +0 -6
- package/dist_ts/http/models/http-types.js +0 -7
- package/dist_ts/http/router/index.d.ts +0 -8
- package/dist_ts/http/router/index.js +0 -7
- package/dist_ts/http/router/proxy-router.d.ts +0 -115
- package/dist_ts/http/router/proxy-router.js +0 -325
- package/dist_ts/http/router/route-router.d.ts +0 -108
- package/dist_ts/http/router/route-router.js +0 -393
- package/dist_ts/protocols/tls/constants.d.ts +0 -122
- package/dist_ts/protocols/tls/constants.js +0 -135
- package/dist_ts/protocols/tls/parser.d.ts +0 -53
- package/dist_ts/protocols/tls/parser.js +0 -294
- package/dist_ts/protocols/tls/types.d.ts +0 -65
- package/dist_ts/protocols/tls/types.js +0 -5
- package/dist_ts/proxies/http-proxy/certificate-manager.d.ts +0 -95
- package/dist_ts/proxies/http-proxy/certificate-manager.js +0 -214
- package/dist_ts/proxies/http-proxy/connection-pool.d.ts +0 -47
- package/dist_ts/proxies/http-proxy/connection-pool.js +0 -195
- package/dist_ts/proxies/http-proxy/context-creator.d.ts +0 -34
- package/dist_ts/proxies/http-proxy/context-creator.js +0 -108
- package/dist_ts/proxies/http-proxy/default-certificates.d.ts +0 -54
- package/dist_ts/proxies/http-proxy/default-certificates.js +0 -127
- package/dist_ts/proxies/http-proxy/function-cache.d.ts +0 -95
- package/dist_ts/proxies/http-proxy/function-cache.js +0 -215
- package/dist_ts/proxies/http-proxy/handlers/index.d.ts +0 -4
- package/dist_ts/proxies/http-proxy/handlers/index.js +0 -6
- package/dist_ts/proxies/http-proxy/handlers/redirect-handler.d.ts +0 -18
- package/dist_ts/proxies/http-proxy/handlers/redirect-handler.js +0 -78
- package/dist_ts/proxies/http-proxy/handlers/static-handler.d.ts +0 -19
- package/dist_ts/proxies/http-proxy/handlers/static-handler.js +0 -211
- package/dist_ts/proxies/http-proxy/http-proxy.d.ts +0 -117
- package/dist_ts/proxies/http-proxy/http-proxy.js +0 -521
- package/dist_ts/proxies/http-proxy/http-request-handler.d.ts +0 -40
- package/dist_ts/proxies/http-proxy/http-request-handler.js +0 -257
- package/dist_ts/proxies/http-proxy/http2-request-handler.d.ts +0 -24
- package/dist_ts/proxies/http-proxy/http2-request-handler.js +0 -201
- package/dist_ts/proxies/http-proxy/index.d.ts +0 -14
- package/dist_ts/proxies/http-proxy/index.js +0 -16
- package/dist_ts/proxies/http-proxy/models/http-types.d.ts +0 -117
- package/dist_ts/proxies/http-proxy/models/http-types.js +0 -92
- package/dist_ts/proxies/http-proxy/models/index.d.ts +0 -5
- package/dist_ts/proxies/http-proxy/models/index.js +0 -6
- package/dist_ts/proxies/http-proxy/models/types.d.ts +0 -75
- package/dist_ts/proxies/http-proxy/models/types.js +0 -35
- package/dist_ts/proxies/http-proxy/request-handler.d.ts +0 -97
- package/dist_ts/proxies/http-proxy/request-handler.js +0 -737
- package/dist_ts/proxies/http-proxy/security-manager.d.ts +0 -98
- package/dist_ts/proxies/http-proxy/security-manager.js +0 -341
- package/dist_ts/proxies/http-proxy/websocket-handler.d.ts +0 -50
- package/dist_ts/proxies/http-proxy/websocket-handler.js +0 -505
- package/dist_ts/proxies/smart-proxy/acme-state-manager.d.ts +0 -42
- package/dist_ts/proxies/smart-proxy/acme-state-manager.js +0 -101
- package/dist_ts/proxies/smart-proxy/cert-store.d.ts +0 -10
- package/dist_ts/proxies/smart-proxy/cert-store.js +0 -72
- package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +0 -164
- package/dist_ts/proxies/smart-proxy/certificate-manager.js +0 -745
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +0 -128
- package/dist_ts/proxies/smart-proxy/connection-manager.js +0 -689
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +0 -43
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +0 -180
- package/dist_ts/proxies/smart-proxy/metrics-collector.d.ts +0 -98
- package/dist_ts/proxies/smart-proxy/metrics-collector.js +0 -355
- package/dist_ts/proxies/smart-proxy/nftables-manager.d.ts +0 -82
- package/dist_ts/proxies/smart-proxy/nftables-manager.js +0 -237
- package/dist_ts/proxies/smart-proxy/port-manager.d.ts +0 -117
- package/dist_ts/proxies/smart-proxy/port-manager.js +0 -318
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +0 -60
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +0 -1407
- package/dist_ts/proxies/smart-proxy/route-manager.d.ts +0 -112
- package/dist_ts/proxies/smart-proxy/route-manager.js +0 -453
- package/dist_ts/proxies/smart-proxy/route-orchestrator.d.ts +0 -56
- package/dist_ts/proxies/smart-proxy/route-orchestrator.js +0 -204
- package/dist_ts/proxies/smart-proxy/rust-binary-locator.d.ts +0 -23
- package/dist_ts/proxies/smart-proxy/rust-binary-locator.js +0 -104
- package/dist_ts/proxies/smart-proxy/security-manager.d.ts +0 -74
- package/dist_ts/proxies/smart-proxy/security-manager.js +0 -227
- package/dist_ts/proxies/smart-proxy/throughput-tracker.d.ts +0 -36
- package/dist_ts/proxies/smart-proxy/throughput-tracker.js +0 -115
- package/dist_ts/proxies/smart-proxy/timeout-manager.d.ts +0 -48
- package/dist_ts/proxies/smart-proxy/timeout-manager.js +0 -158
- package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +0 -50
- package/dist_ts/proxies/smart-proxy/tls-manager.js +0 -110
- package/dist_ts/proxies/smart-proxy/utils/route-patterns.d.ts +0 -161
- package/dist_ts/proxies/smart-proxy/utils/route-patterns.js +0 -282
- package/dist_ts/proxies/smart-proxy/utils/route-validators.d.ts +0 -73
- package/dist_ts/proxies/smart-proxy/utils/route-validators.js +0 -259
- package/dist_ts/routing/router/proxy-router.d.ts +0 -115
- package/dist_ts/routing/router/proxy-router.js +0 -325
- package/dist_ts/routing/router/route-router.d.ts +0 -108
- package/dist_ts/routing/router/route-router.js +0 -393
- package/dist_ts/tls/alerts/index.d.ts +0 -4
- package/dist_ts/tls/alerts/index.js +0 -5
- package/dist_ts/tls/alerts/tls-alert.d.ts +0 -150
- package/dist_ts/tls/alerts/tls-alert.js +0 -226
- package/dist_ts/tls/sni/client-hello-parser.d.ts +0 -100
- package/dist_ts/tls/sni/client-hello-parser.js +0 -464
- package/dist_ts/tls/sni/sni-extraction.d.ts +0 -58
- package/dist_ts/tls/sni/sni-extraction.js +0 -275
- package/dist_ts/tls/utils/index.d.ts +0 -4
- package/dist_ts/tls/utils/index.js +0 -5
- package/dist_ts/tls/utils/tls-utils.d.ts +0 -49
- package/dist_ts/tls/utils/tls-utils.js +0 -75
- package/ts/proxies/smart-proxy/rust-binary-locator.ts +0 -112
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartproxy",
|
|
3
|
-
"version": "23.
|
|
3
|
+
"version": "23.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -10,16 +10,17 @@
|
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"scripts": {
|
|
12
12
|
"test": "(tstest test/**/test*.ts --verbose --timeout 60 --logfile)",
|
|
13
|
-
"build": "(tsbuild tsfolders --allowimplicitany)",
|
|
13
|
+
"build": "(tsbuild tsfolders --allowimplicitany) && (tsrust)",
|
|
14
14
|
"format": "(gitzone format)",
|
|
15
15
|
"buildDocs": "tsdoc"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@git.zone/tsbuild": "^
|
|
19
|
-
"@git.zone/tsrun": "^2.0.
|
|
20
|
-
"@git.zone/
|
|
21
|
-
"@
|
|
22
|
-
"@
|
|
18
|
+
"@git.zone/tsbuild": "^4.1.2",
|
|
19
|
+
"@git.zone/tsrun": "^2.0.1",
|
|
20
|
+
"@git.zone/tsrust": "^1.3.0",
|
|
21
|
+
"@git.zone/tstest": "^3.1.8",
|
|
22
|
+
"@push.rocks/smartserve": "^2.0.1",
|
|
23
|
+
"@types/node": "^25.2.2",
|
|
23
24
|
"typescript": "^5.9.3",
|
|
24
25
|
"why-is-node-running": "^3.2.2"
|
|
25
26
|
},
|
|
@@ -28,20 +29,21 @@
|
|
|
28
29
|
"@push.rocks/smartacme": "^8.0.0",
|
|
29
30
|
"@push.rocks/smartcrypto": "^2.0.4",
|
|
30
31
|
"@push.rocks/smartdelay": "^3.0.5",
|
|
31
|
-
"@push.rocks/smartfile": "^13.1.
|
|
32
|
+
"@push.rocks/smartfile": "^13.1.2",
|
|
32
33
|
"@push.rocks/smartlog": "^3.1.10",
|
|
33
34
|
"@push.rocks/smartnetwork": "^4.4.0",
|
|
34
35
|
"@push.rocks/smartpromise": "^4.2.3",
|
|
35
36
|
"@push.rocks/smartrequest": "^5.0.1",
|
|
37
|
+
"@push.rocks/smartrust": "^1.1.1",
|
|
36
38
|
"@push.rocks/smartrx": "^3.0.10",
|
|
37
39
|
"@push.rocks/smartstring": "^4.1.0",
|
|
38
|
-
"@push.rocks/taskbuffer": "^
|
|
40
|
+
"@push.rocks/taskbuffer": "^4.2.0",
|
|
39
41
|
"@tsclass/tsclass": "^9.3.0",
|
|
40
42
|
"@types/minimatch": "^6.0.0",
|
|
41
43
|
"@types/ws": "^8.18.1",
|
|
42
|
-
"minimatch": "^10.1.
|
|
44
|
+
"minimatch": "^10.1.2",
|
|
43
45
|
"pretty-ms": "^9.3.0",
|
|
44
|
-
"ws": "^8.
|
|
46
|
+
"ws": "^8.19.0"
|
|
45
47
|
},
|
|
46
48
|
"files": [
|
|
47
49
|
"ts/**/*",
|
package/readme.md
CHANGED
|
@@ -378,7 +378,9 @@ await proxy.updateRoutes([...newRoutes]);
|
|
|
378
378
|
// Get real-time metrics
|
|
379
379
|
const metrics = proxy.getMetrics();
|
|
380
380
|
console.log(`Active connections: ${metrics.connections.active()}`);
|
|
381
|
-
console.log(`
|
|
381
|
+
console.log(`Bytes in: ${metrics.totals.bytesIn()}`);
|
|
382
|
+
console.log(`Requests/sec: ${metrics.requests.perSecond()}`);
|
|
383
|
+
console.log(`Throughput in: ${metrics.throughput.instant().in} bytes/sec`);
|
|
382
384
|
|
|
383
385
|
// Get detailed statistics from the Rust engine
|
|
384
386
|
const stats = await proxy.getStatistics();
|
|
@@ -486,8 +488,8 @@ SmartProxy uses a hybrid **Rust + TypeScript** architecture:
|
|
|
486
488
|
|
|
487
489
|
- **Rust Engine** handles all networking, TLS, HTTP proxying, connection management, security, and metrics
|
|
488
490
|
- **TypeScript** provides the npm API, configuration types, route helpers, validation, and socket handler callbacks
|
|
489
|
-
- **IPC** — JSON commands/events over stdin/stdout
|
|
490
|
-
- **Socket Relay** —
|
|
491
|
+
- **IPC** — The TypeScript wrapper uses [`@push.rocks/smartrust`](https://code.foss.global/push.rocks/smartrust) for type-safe JSON commands/events over stdin/stdout
|
|
492
|
+
- **Socket Relay** — A Unix domain socket server for routes requiring TypeScript-side handling (socket handlers, dynamic host/port functions)
|
|
491
493
|
|
|
492
494
|
## 🎯 Route Configuration Reference
|
|
493
495
|
|
|
@@ -699,7 +701,7 @@ interface ISmartProxyOptions {
|
|
|
699
701
|
|
|
700
702
|
// Timeouts
|
|
701
703
|
connectionTimeout?: number; // Backend connection timeout (default: 30s)
|
|
702
|
-
initialDataTimeout?: number; // Initial data/SNI timeout (default:
|
|
704
|
+
initialDataTimeout?: number; // Initial data/SNI timeout (default: 120s)
|
|
703
705
|
socketTimeout?: number; // Socket inactivity timeout (default: 1h)
|
|
704
706
|
maxConnectionLifetime?: number; // Max connection lifetime (default: 24h)
|
|
705
707
|
inactivityTimeout?: number; // Inactivity timeout (default: 4h)
|
|
@@ -724,12 +726,40 @@ interface ISmartProxyOptions {
|
|
|
724
726
|
// Behavior
|
|
725
727
|
enableDetailedLogging?: boolean; // Verbose connection logging
|
|
726
728
|
enableTlsDebugLogging?: boolean; // TLS handshake debug logging
|
|
727
|
-
|
|
728
|
-
// Rust binary
|
|
729
|
-
rustBinaryPath?: string; // Custom path to the Rust binary
|
|
730
729
|
}
|
|
731
730
|
```
|
|
732
731
|
|
|
732
|
+
### IMetrics Interface
|
|
733
|
+
|
|
734
|
+
The `getMetrics()` method returns a cached metrics adapter that polls the Rust engine:
|
|
735
|
+
|
|
736
|
+
```typescript
|
|
737
|
+
const metrics = proxy.getMetrics();
|
|
738
|
+
|
|
739
|
+
// Connection metrics
|
|
740
|
+
metrics.connections.active(); // Current active connections
|
|
741
|
+
metrics.connections.total(); // Total connections since start
|
|
742
|
+
metrics.connections.byRoute(); // Map<routeName, activeCount>
|
|
743
|
+
metrics.connections.byIP(); // Map<ip, activeCount>
|
|
744
|
+
metrics.connections.topIPs(10); // Top N IPs by connection count
|
|
745
|
+
|
|
746
|
+
// Throughput (bytes/sec)
|
|
747
|
+
metrics.throughput.instant(); // { in: number, out: number }
|
|
748
|
+
metrics.throughput.recent(); // Recent average
|
|
749
|
+
metrics.throughput.average(); // Overall average
|
|
750
|
+
metrics.throughput.byRoute(); // Map<routeName, { in, out }>
|
|
751
|
+
|
|
752
|
+
// Request rates
|
|
753
|
+
metrics.requests.perSecond(); // Requests per second
|
|
754
|
+
metrics.requests.perMinute(); // Requests per minute
|
|
755
|
+
metrics.requests.total(); // Total requests
|
|
756
|
+
|
|
757
|
+
// Cumulative totals
|
|
758
|
+
metrics.totals.bytesIn(); // Total bytes received
|
|
759
|
+
metrics.totals.bytesOut(); // Total bytes sent
|
|
760
|
+
metrics.totals.connections(); // Total connections
|
|
761
|
+
```
|
|
762
|
+
|
|
733
763
|
## 🐛 Troubleshooting
|
|
734
764
|
|
|
735
765
|
### Certificate Issues
|
|
@@ -746,13 +776,13 @@ interface ISmartProxyOptions {
|
|
|
746
776
|
- ✅ Enable debug logging with `enableDetailedLogging: true`
|
|
747
777
|
|
|
748
778
|
### Rust Binary Not Found
|
|
779
|
+
|
|
749
780
|
SmartProxy searches for the Rust binary in this order:
|
|
750
781
|
1. `SMARTPROXY_RUST_BINARY` environment variable
|
|
751
782
|
2. Platform-specific npm package (`@push.rocks/smartproxy-linux-x64`, etc.)
|
|
752
|
-
3.
|
|
753
|
-
4.
|
|
754
|
-
|
|
755
|
-
Set `rustBinaryPath` in options to override.
|
|
783
|
+
3. `dist_rust/rustproxy` relative to the package root (built by `tsrust`)
|
|
784
|
+
4. Local dev build (`./rust/target/release/rustproxy`)
|
|
785
|
+
5. System PATH (`rustproxy`)
|
|
756
786
|
|
|
757
787
|
### Performance Tuning
|
|
758
788
|
- ✅ Use NFTables forwarding for high-traffic routes (Linux only)
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '23.
|
|
6
|
+
version: '23.1.0',
|
|
7
7
|
description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
|
|
8
8
|
}
|
package/ts/plugins.ts
CHANGED
|
@@ -31,6 +31,7 @@ import * as smartlog from '@push.rocks/smartlog';
|
|
|
31
31
|
import * as smartlogDestinationLocal from '@push.rocks/smartlog/destination-local';
|
|
32
32
|
import * as taskbuffer from '@push.rocks/taskbuffer';
|
|
33
33
|
import * as smartrx from '@push.rocks/smartrx';
|
|
34
|
+
import * as smartrust from '@push.rocks/smartrust';
|
|
34
35
|
|
|
35
36
|
export {
|
|
36
37
|
lik,
|
|
@@ -47,6 +48,7 @@ export {
|
|
|
47
48
|
smartlogDestinationLocal,
|
|
48
49
|
taskbuffer,
|
|
49
50
|
smartrx,
|
|
51
|
+
smartrust,
|
|
50
52
|
};
|
|
51
53
|
|
|
52
54
|
// third party scope
|
|
@@ -1,310 +1,179 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
import { logger } from '../../core/utils/logger.js';
|
|
3
|
-
import { RustBinaryLocator } from './rust-binary-locator.js';
|
|
4
3
|
import type { IRouteConfig } from './models/route-types.js';
|
|
5
|
-
import { ChildProcess, spawn } from 'child_process';
|
|
6
|
-
import { createInterface, Interface as ReadlineInterface } from 'readline';
|
|
7
4
|
|
|
8
5
|
/**
|
|
9
|
-
*
|
|
6
|
+
* Type-safe command definitions for the Rust proxy IPC protocol.
|
|
10
7
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
params:
|
|
8
|
+
type TSmartProxyCommands = {
|
|
9
|
+
start: { params: { config: any }; result: void };
|
|
10
|
+
stop: { params: Record<string, never>; result: void };
|
|
11
|
+
updateRoutes: { params: { routes: IRouteConfig[] }; result: void };
|
|
12
|
+
getMetrics: { params: Record<string, never>; result: any };
|
|
13
|
+
getStatistics: { params: Record<string, never>; result: any };
|
|
14
|
+
provisionCertificate: { params: { routeName: string }; result: void };
|
|
15
|
+
renewCertificate: { params: { routeName: string }; result: void };
|
|
16
|
+
getCertificateStatus: { params: { routeName: string }; result: any };
|
|
17
|
+
getListeningPorts: { params: Record<string, never>; result: { ports: number[] } };
|
|
18
|
+
getNftablesStatus: { params: Record<string, never>; result: any };
|
|
19
|
+
setSocketHandlerRelay: { params: { socketPath: string }; result: void };
|
|
20
|
+
addListeningPort: { params: { port: number }; result: void };
|
|
21
|
+
removeListeningPort: { params: { port: number }; result: void };
|
|
22
|
+
loadCertificate: { params: { domain: string; cert: string; key: string; ca?: string }; result: void };
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get the package root directory using import.meta.url.
|
|
27
|
+
* This file is at ts/proxies/smart-proxy/, so package root is 3 levels up.
|
|
28
|
+
*/
|
|
29
|
+
function getPackageRoot(): string {
|
|
30
|
+
const thisDir = plugins.path.dirname(plugins.url.fileURLToPath(import.meta.url));
|
|
31
|
+
return plugins.path.resolve(thisDir, '..', '..', '..');
|
|
15
32
|
}
|
|
16
33
|
|
|
17
34
|
/**
|
|
18
|
-
*
|
|
35
|
+
* Map Node.js process.platform/process.arch to tsrust's friendly name suffix.
|
|
36
|
+
* tsrust names cross-compiled binaries as: rustproxy_linux_amd64, rustproxy_linux_arm64, etc.
|
|
19
37
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
function getTsrustPlatformSuffix(): string | null {
|
|
39
|
+
const archMap: Record<string, string> = { x64: 'amd64', arm64: 'arm64' };
|
|
40
|
+
const osMap: Record<string, string> = { linux: 'linux', darwin: 'macos' };
|
|
41
|
+
const os = osMap[process.platform];
|
|
42
|
+
const arch = archMap[process.arch];
|
|
43
|
+
if (os && arch) {
|
|
44
|
+
return `${os}_${arch}`;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
25
47
|
}
|
|
26
48
|
|
|
27
49
|
/**
|
|
28
|
-
*
|
|
50
|
+
* Build local search paths for the Rust binary, including dist_rust/ candidates
|
|
51
|
+
* (built by tsrust) and local development build paths.
|
|
29
52
|
*/
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
53
|
+
function buildLocalPaths(): string[] {
|
|
54
|
+
const packageRoot = getPackageRoot();
|
|
55
|
+
const suffix = getTsrustPlatformSuffix();
|
|
56
|
+
const paths: string[] = [];
|
|
57
|
+
|
|
58
|
+
// dist_rust/ candidates (tsrust cross-compiled output)
|
|
59
|
+
if (suffix) {
|
|
60
|
+
paths.push(plugins.path.join(packageRoot, 'dist_rust', `rustproxy_${suffix}`));
|
|
61
|
+
}
|
|
62
|
+
paths.push(plugins.path.join(packageRoot, 'dist_rust', 'rustproxy'));
|
|
63
|
+
|
|
64
|
+
// Local dev build paths
|
|
65
|
+
paths.push(plugins.path.resolve(process.cwd(), 'rust', 'target', 'release', 'rustproxy'));
|
|
66
|
+
paths.push(plugins.path.resolve(process.cwd(), 'rust', 'target', 'debug', 'rustproxy'));
|
|
67
|
+
|
|
68
|
+
return paths;
|
|
33
69
|
}
|
|
34
70
|
|
|
35
71
|
/**
|
|
36
72
|
* Bridge between TypeScript SmartProxy and the Rust binary.
|
|
37
|
-
*
|
|
73
|
+
* Wraps @push.rocks/smartrust's RustBridge with type-safe command definitions.
|
|
38
74
|
*/
|
|
39
75
|
export class RustProxyBridge extends plugins.EventEmitter {
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
76
|
+
private bridge: plugins.smartrust.RustBridge<TSmartProxyCommands>;
|
|
77
|
+
|
|
78
|
+
constructor() {
|
|
79
|
+
super();
|
|
80
|
+
|
|
81
|
+
this.bridge = new plugins.smartrust.RustBridge<TSmartProxyCommands>({
|
|
82
|
+
binaryName: 'rustproxy',
|
|
83
|
+
envVarName: 'SMARTPROXY_RUST_BINARY',
|
|
84
|
+
platformPackagePrefix: '@push.rocks/smartproxy',
|
|
85
|
+
localPaths: buildLocalPaths(),
|
|
86
|
+
logger: {
|
|
87
|
+
log: (level: string, message: string, data?: Record<string, any>) => {
|
|
88
|
+
logger.log(level as any, message, data);
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Forward events from the inner bridge
|
|
94
|
+
this.bridge.on('exit', (code: number | null, signal: string | null) => {
|
|
95
|
+
this.emit('exit', code, signal);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
52
98
|
|
|
53
99
|
/**
|
|
54
100
|
* Spawn the Rust binary in management mode.
|
|
55
101
|
* Returns true if the binary was found and spawned successfully.
|
|
56
102
|
*/
|
|
57
103
|
public async spawn(): Promise<boolean> {
|
|
58
|
-
|
|
59
|
-
if (!this.binaryPath) {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return new Promise<boolean>((resolve) => {
|
|
64
|
-
try {
|
|
65
|
-
this.process = spawn(this.binaryPath!, ['--management'], {
|
|
66
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
67
|
-
env: { ...process.env },
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// Handle stderr (logging from Rust goes here)
|
|
71
|
-
const stderrHandler = (data: Buffer) => {
|
|
72
|
-
const lines = data.toString().split('\n').filter(l => l.trim());
|
|
73
|
-
for (const line of lines) {
|
|
74
|
-
logger.log('debug', `[rustproxy] ${line}`, { component: 'rust-bridge' });
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
this.process.stderr?.on('data', stderrHandler);
|
|
78
|
-
|
|
79
|
-
// Handle stdout (JSON IPC)
|
|
80
|
-
this.readline = createInterface({ input: this.process.stdout! });
|
|
81
|
-
this.readline.on('line', (line: string) => {
|
|
82
|
-
this.handleLine(line.trim());
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// Handle process exit
|
|
86
|
-
this.process.on('exit', (code, signal) => {
|
|
87
|
-
logger.log('info', `RustProxy process exited (code=${code}, signal=${signal})`, { component: 'rust-bridge' });
|
|
88
|
-
this.cleanup();
|
|
89
|
-
this.emit('exit', code, signal);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
this.process.on('error', (err) => {
|
|
93
|
-
logger.log('error', `RustProxy process error: ${err.message}`, { component: 'rust-bridge' });
|
|
94
|
-
this.cleanup();
|
|
95
|
-
resolve(false);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// Wait for the 'ready' event from Rust
|
|
99
|
-
const readyTimeout = setTimeout(() => {
|
|
100
|
-
logger.log('error', 'RustProxy did not send ready event within 10s', { component: 'rust-bridge' });
|
|
101
|
-
this.kill();
|
|
102
|
-
resolve(false);
|
|
103
|
-
}, 10000);
|
|
104
|
-
|
|
105
|
-
this.once('management:ready', () => {
|
|
106
|
-
clearTimeout(readyTimeout);
|
|
107
|
-
this.isRunning = true;
|
|
108
|
-
logger.log('info', 'RustProxy bridge connected', { component: 'rust-bridge' });
|
|
109
|
-
resolve(true);
|
|
110
|
-
});
|
|
111
|
-
} catch (err: any) {
|
|
112
|
-
logger.log('error', `Failed to spawn RustProxy: ${err.message}`, { component: 'rust-bridge' });
|
|
113
|
-
resolve(false);
|
|
114
|
-
}
|
|
115
|
-
});
|
|
104
|
+
return this.bridge.spawn();
|
|
116
105
|
}
|
|
117
106
|
|
|
118
107
|
/**
|
|
119
|
-
*
|
|
108
|
+
* Kill the Rust process and clean up.
|
|
120
109
|
*/
|
|
121
|
-
public
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const id = `req_${++this.requestCounter}`;
|
|
127
|
-
const request: IManagementRequest = { id, method, params };
|
|
128
|
-
|
|
129
|
-
return new Promise<any>((resolve, reject) => {
|
|
130
|
-
const timer = setTimeout(() => {
|
|
131
|
-
this.pendingRequests.delete(id);
|
|
132
|
-
reject(new Error(`RustProxy command '${method}' timed out after ${this.requestTimeoutMs}ms`));
|
|
133
|
-
}, this.requestTimeoutMs);
|
|
134
|
-
|
|
135
|
-
this.pendingRequests.set(id, { resolve, reject, timer });
|
|
110
|
+
public kill(): void {
|
|
111
|
+
this.bridge.kill();
|
|
112
|
+
}
|
|
136
113
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
reject(new Error(`Failed to write to RustProxy stdin: ${err.message}`));
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
});
|
|
114
|
+
/**
|
|
115
|
+
* Whether the bridge is currently running.
|
|
116
|
+
*/
|
|
117
|
+
public get running(): boolean {
|
|
118
|
+
return this.bridge.running;
|
|
146
119
|
}
|
|
147
120
|
|
|
148
|
-
// Convenience methods for each management command
|
|
121
|
+
// --- Convenience methods for each management command ---
|
|
149
122
|
|
|
150
123
|
public async startProxy(config: any): Promise<void> {
|
|
151
|
-
await this.sendCommand('start', { config });
|
|
124
|
+
await this.bridge.sendCommand('start', { config });
|
|
152
125
|
}
|
|
153
126
|
|
|
154
127
|
public async stopProxy(): Promise<void> {
|
|
155
|
-
await this.sendCommand('stop');
|
|
128
|
+
await this.bridge.sendCommand('stop', {} as Record<string, never>);
|
|
156
129
|
}
|
|
157
130
|
|
|
158
131
|
public async updateRoutes(routes: IRouteConfig[]): Promise<void> {
|
|
159
|
-
await this.sendCommand('updateRoutes', { routes });
|
|
132
|
+
await this.bridge.sendCommand('updateRoutes', { routes });
|
|
160
133
|
}
|
|
161
134
|
|
|
162
135
|
public async getMetrics(): Promise<any> {
|
|
163
|
-
return this.sendCommand('getMetrics');
|
|
136
|
+
return this.bridge.sendCommand('getMetrics', {} as Record<string, never>);
|
|
164
137
|
}
|
|
165
138
|
|
|
166
139
|
public async getStatistics(): Promise<any> {
|
|
167
|
-
return this.sendCommand('getStatistics');
|
|
140
|
+
return this.bridge.sendCommand('getStatistics', {} as Record<string, never>);
|
|
168
141
|
}
|
|
169
142
|
|
|
170
143
|
public async provisionCertificate(routeName: string): Promise<void> {
|
|
171
|
-
await this.sendCommand('provisionCertificate', { routeName });
|
|
144
|
+
await this.bridge.sendCommand('provisionCertificate', { routeName });
|
|
172
145
|
}
|
|
173
146
|
|
|
174
147
|
public async renewCertificate(routeName: string): Promise<void> {
|
|
175
|
-
await this.sendCommand('renewCertificate', { routeName });
|
|
148
|
+
await this.bridge.sendCommand('renewCertificate', { routeName });
|
|
176
149
|
}
|
|
177
150
|
|
|
178
151
|
public async getCertificateStatus(routeName: string): Promise<any> {
|
|
179
|
-
return this.sendCommand('getCertificateStatus', { routeName });
|
|
152
|
+
return this.bridge.sendCommand('getCertificateStatus', { routeName });
|
|
180
153
|
}
|
|
181
154
|
|
|
182
155
|
public async getListeningPorts(): Promise<number[]> {
|
|
183
|
-
const result = await this.sendCommand('getListeningPorts');
|
|
156
|
+
const result = await this.bridge.sendCommand('getListeningPorts', {} as Record<string, never>);
|
|
184
157
|
return result?.ports ?? [];
|
|
185
158
|
}
|
|
186
159
|
|
|
187
160
|
public async getNftablesStatus(): Promise<any> {
|
|
188
|
-
return this.sendCommand('getNftablesStatus');
|
|
161
|
+
return this.bridge.sendCommand('getNftablesStatus', {} as Record<string, never>);
|
|
189
162
|
}
|
|
190
163
|
|
|
191
164
|
public async setSocketHandlerRelay(socketPath: string): Promise<void> {
|
|
192
|
-
await this.sendCommand('setSocketHandlerRelay', { socketPath });
|
|
165
|
+
await this.bridge.sendCommand('setSocketHandlerRelay', { socketPath });
|
|
193
166
|
}
|
|
194
167
|
|
|
195
168
|
public async addListeningPort(port: number): Promise<void> {
|
|
196
|
-
await this.sendCommand('addListeningPort', { port });
|
|
169
|
+
await this.bridge.sendCommand('addListeningPort', { port });
|
|
197
170
|
}
|
|
198
171
|
|
|
199
172
|
public async removeListeningPort(port: number): Promise<void> {
|
|
200
|
-
await this.sendCommand('removeListeningPort', { port });
|
|
173
|
+
await this.bridge.sendCommand('removeListeningPort', { port });
|
|
201
174
|
}
|
|
202
175
|
|
|
203
176
|
public async loadCertificate(domain: string, cert: string, key: string, ca?: string): Promise<void> {
|
|
204
|
-
await this.sendCommand('loadCertificate', { domain, cert, key, ca });
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Kill the Rust process and clean up all stdio streams.
|
|
209
|
-
*/
|
|
210
|
-
public kill(): void {
|
|
211
|
-
if (this.process) {
|
|
212
|
-
const proc = this.process;
|
|
213
|
-
this.process = null;
|
|
214
|
-
this.isRunning = false;
|
|
215
|
-
|
|
216
|
-
// Close readline (reads from stdout)
|
|
217
|
-
if (this.readline) {
|
|
218
|
-
this.readline.close();
|
|
219
|
-
this.readline = null;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Reject pending requests
|
|
223
|
-
for (const [, pending] of this.pendingRequests) {
|
|
224
|
-
clearTimeout(pending.timer);
|
|
225
|
-
pending.reject(new Error('RustProxy process killed'));
|
|
226
|
-
}
|
|
227
|
-
this.pendingRequests.clear();
|
|
228
|
-
|
|
229
|
-
// Remove all listeners so nothing keeps references
|
|
230
|
-
proc.removeAllListeners();
|
|
231
|
-
proc.stdout?.removeAllListeners();
|
|
232
|
-
proc.stderr?.removeAllListeners();
|
|
233
|
-
proc.stdin?.removeAllListeners();
|
|
234
|
-
|
|
235
|
-
// Kill the process
|
|
236
|
-
try { proc.kill('SIGTERM'); } catch { /* already dead */ }
|
|
237
|
-
|
|
238
|
-
// Destroy all stdio pipes to free handles
|
|
239
|
-
try { proc.stdin?.destroy(); } catch { /* ignore */ }
|
|
240
|
-
try { proc.stdout?.destroy(); } catch { /* ignore */ }
|
|
241
|
-
try { proc.stderr?.destroy(); } catch { /* ignore */ }
|
|
242
|
-
|
|
243
|
-
// Unref process so Node doesn't wait for it
|
|
244
|
-
try { proc.unref(); } catch { /* ignore */ }
|
|
245
|
-
|
|
246
|
-
// Force kill after 5 seconds
|
|
247
|
-
setTimeout(() => {
|
|
248
|
-
try { proc.kill('SIGKILL'); } catch { /* already dead */ }
|
|
249
|
-
}, 5000).unref();
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Whether the bridge is currently running.
|
|
255
|
-
*/
|
|
256
|
-
public get running(): boolean {
|
|
257
|
-
return this.isRunning;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
private handleLine(line: string): void {
|
|
261
|
-
if (!line) return;
|
|
262
|
-
|
|
263
|
-
let parsed: any;
|
|
264
|
-
try {
|
|
265
|
-
parsed = JSON.parse(line);
|
|
266
|
-
} catch {
|
|
267
|
-
logger.log('warn', `Non-JSON output from RustProxy: ${line}`, { component: 'rust-bridge' });
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Check if it's an event (has 'event' field)
|
|
272
|
-
if ('event' in parsed) {
|
|
273
|
-
const event = parsed as IManagementEvent;
|
|
274
|
-
this.emit(`management:${event.event}`, event.data);
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Otherwise it's a response (has 'id' field)
|
|
279
|
-
if ('id' in parsed) {
|
|
280
|
-
const response = parsed as IManagementResponse;
|
|
281
|
-
const pending = this.pendingRequests.get(response.id);
|
|
282
|
-
if (pending) {
|
|
283
|
-
clearTimeout(pending.timer);
|
|
284
|
-
this.pendingRequests.delete(response.id);
|
|
285
|
-
if (response.success) {
|
|
286
|
-
pending.resolve(response.result);
|
|
287
|
-
} else {
|
|
288
|
-
pending.reject(new Error(response.error || 'Unknown error from RustProxy'));
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
private cleanup(): void {
|
|
295
|
-
this.isRunning = false;
|
|
296
|
-
this.process = null;
|
|
297
|
-
|
|
298
|
-
if (this.readline) {
|
|
299
|
-
this.readline.close();
|
|
300
|
-
this.readline = null;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Reject all pending requests
|
|
304
|
-
for (const [id, pending] of this.pendingRequests) {
|
|
305
|
-
clearTimeout(pending.timer);
|
|
306
|
-
pending.reject(new Error('RustProxy process exited'));
|
|
307
|
-
}
|
|
308
|
-
this.pendingRequests.clear();
|
|
177
|
+
await this.bridge.sendCommand('loadCertificate', { domain, cert, key, ca });
|
|
309
178
|
}
|
|
310
179
|
}
|
|
@@ -3,7 +3,6 @@ import { logger } from '../../core/utils/logger.js';
|
|
|
3
3
|
|
|
4
4
|
// Rust bridge and helpers
|
|
5
5
|
import { RustProxyBridge } from './rust-proxy-bridge.js';
|
|
6
|
-
import { RustBinaryLocator } from './rust-binary-locator.js';
|
|
7
6
|
import { RoutePreprocessor } from './route-preprocessor.js';
|
|
8
7
|
import { SocketHandlerServer } from './socket-handler-server.js';
|
|
9
8
|
import { RustMetricsAdapter } from './rust-metrics-adapter.js';
|
|
@@ -120,7 +119,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
120
119
|
if (!spawned) {
|
|
121
120
|
throw new Error(
|
|
122
121
|
'RustProxy binary not found. Set SMARTPROXY_RUST_BINARY env var, install the platform package, ' +
|
|
123
|
-
'or build locally with:
|
|
122
|
+
'or build locally with: pnpm build'
|
|
124
123
|
);
|
|
125
124
|
}
|
|
126
125
|
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { ICertificateData, ICertificateFailure, ICertificateExpiring } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Subscribers callback definitions for Port80Handler events
|
|
4
|
-
*/
|
|
5
|
-
export interface Port80HandlerSubscribers {
|
|
6
|
-
onCertificateIssued?: (data: ICertificateData) => void;
|
|
7
|
-
onCertificateRenewed?: (data: ICertificateData) => void;
|
|
8
|
-
onCertificateFailed?: (data: ICertificateFailure) => void;
|
|
9
|
-
onCertificateExpiring?: (data: ICertificateExpiring) => void;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Subscribes to Port80Handler events based on provided callbacks
|
|
13
|
-
*/
|
|
14
|
-
export declare function subscribeToPort80Handler(handler: any, subscribers: Port80HandlerSubscribers): void;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// Port80Handler removed - use SmartCertManager instead
|
|
2
|
-
import { Port80HandlerEvents } from './types.js';
|
|
3
|
-
/**
|
|
4
|
-
* Subscribes to Port80Handler events based on provided callbacks
|
|
5
|
-
*/
|
|
6
|
-
export function subscribeToPort80Handler(handler, subscribers) {
|
|
7
|
-
if (subscribers.onCertificateIssued) {
|
|
8
|
-
handler.on(Port80HandlerEvents.CERTIFICATE_ISSUED, subscribers.onCertificateIssued);
|
|
9
|
-
}
|
|
10
|
-
if (subscribers.onCertificateRenewed) {
|
|
11
|
-
handler.on(Port80HandlerEvents.CERTIFICATE_RENEWED, subscribers.onCertificateRenewed);
|
|
12
|
-
}
|
|
13
|
-
if (subscribers.onCertificateFailed) {
|
|
14
|
-
handler.on(Port80HandlerEvents.CERTIFICATE_FAILED, subscribers.onCertificateFailed);
|
|
15
|
-
}
|
|
16
|
-
if (subscribers.onCertificateExpiring) {
|
|
17
|
-
handler.on(Port80HandlerEvents.CERTIFICATE_EXPIRING, subscribers.onCertificateExpiring);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnRVdGlscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2NvbW1vbi9ldmVudFV0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHVEQUF1RDtBQUN2RCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFhakQ7O0dBRUc7QUFDSCxNQUFNLFVBQVUsd0JBQXdCLENBQ3RDLE9BQVksRUFDWixXQUFxQztJQUVyQyxJQUFJLFdBQVcsQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO1FBQ3BDLE9BQU8sQ0FBQyxFQUFFLENBQUMsbUJBQW1CLENBQUMsa0JBQWtCLEVBQUUsV0FBVyxDQUFDLG1CQUFtQixDQUFDLENBQUM7SUFDdEYsQ0FBQztJQUNELElBQUksV0FBVyxDQUFDLG9CQUFvQixFQUFFLENBQUM7UUFDckMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQyxtQkFBbUIsRUFBRSxXQUFXLENBQUMsb0JBQW9CLENBQUMsQ0FBQztJQUN4RixDQUFDO0lBQ0QsSUFBSSxXQUFXLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUNwQyxPQUFPLENBQUMsRUFBRSxDQUFDLG1CQUFtQixDQUFDLGtCQUFrQixFQUFFLFdBQVcsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO0lBQ3RGLENBQUM7SUFDRCxJQUFJLFdBQVcsQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1FBQ3RDLE9BQU8sQ0FBQyxFQUFFLENBQUMsbUJBQW1CLENBQUMsb0JBQW9CLEVBQUUsV0FBVyxDQUFDLHFCQUFxQixDQUFDLENBQUM7SUFDMUYsQ0FBQztBQUNILENBQUMifQ==
|