@push.rocks/smartproxy 21.1.7 → 22.6.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.
Files changed (155) hide show
  1. package/changelog.md +109 -0
  2. package/dist_rust/rustproxy +0 -0
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/core/utils/shared-security-manager.d.ts +17 -0
  5. package/dist_ts/core/utils/shared-security-manager.js +66 -1
  6. package/dist_ts/index.d.ts +1 -5
  7. package/dist_ts/index.js +3 -9
  8. package/dist_ts/protocols/common/fragment-handler.js +5 -1
  9. package/dist_ts/proxies/http-proxy/default-certificates.d.ts +54 -0
  10. package/dist_ts/proxies/http-proxy/default-certificates.js +127 -0
  11. package/dist_ts/proxies/http-proxy/http-proxy.d.ts +1 -1
  12. package/dist_ts/proxies/http-proxy/http-proxy.js +9 -14
  13. package/dist_ts/proxies/http-proxy/index.d.ts +5 -1
  14. package/dist_ts/proxies/http-proxy/index.js +6 -2
  15. package/dist_ts/proxies/http-proxy/security-manager.d.ts +4 -12
  16. package/dist_ts/proxies/http-proxy/security-manager.js +66 -99
  17. package/dist_ts/proxies/index.d.ts +1 -5
  18. package/dist_ts/proxies/index.js +2 -6
  19. package/dist_ts/proxies/nftables-proxy/index.d.ts +1 -0
  20. package/dist_ts/proxies/nftables-proxy/index.js +2 -1
  21. package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +4 -26
  22. package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +84 -236
  23. package/dist_ts/proxies/nftables-proxy/utils/index.d.ts +9 -0
  24. package/dist_ts/proxies/nftables-proxy/utils/index.js +12 -0
  25. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.d.ts +66 -0
  26. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.js +131 -0
  27. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.d.ts +39 -0
  28. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.js +112 -0
  29. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.d.ts +59 -0
  30. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.js +130 -0
  31. package/dist_ts/proxies/smart-proxy/certificate-manager.js +4 -3
  32. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +13 -2
  33. package/dist_ts/proxies/smart-proxy/connection-manager.js +16 -6
  34. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +35 -10
  35. package/dist_ts/proxies/smart-proxy/index.d.ts +5 -10
  36. package/dist_ts/proxies/smart-proxy/index.js +7 -13
  37. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -3
  38. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +17 -0
  39. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +72 -9
  40. package/dist_ts/proxies/smart-proxy/route-preprocessor.d.ts +37 -0
  41. package/dist_ts/proxies/smart-proxy/route-preprocessor.js +103 -0
  42. package/dist_ts/proxies/smart-proxy/rust-binary-locator.d.ts +23 -0
  43. package/dist_ts/proxies/smart-proxy/rust-binary-locator.js +104 -0
  44. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.d.ts +74 -0
  45. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +146 -0
  46. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +49 -0
  47. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +259 -0
  48. package/dist_ts/proxies/smart-proxy/security-manager.d.ts +14 -12
  49. package/dist_ts/proxies/smart-proxy/security-manager.js +80 -74
  50. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +39 -157
  51. package/dist_ts/proxies/smart-proxy/smart-proxy.js +224 -622
  52. package/dist_ts/proxies/smart-proxy/socket-handler-server.d.ts +45 -0
  53. package/dist_ts/proxies/smart-proxy/socket-handler-server.js +253 -0
  54. package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +2 -9
  55. package/dist_ts/proxies/smart-proxy/tls-manager.js +3 -26
  56. package/dist_ts/proxies/smart-proxy/utils/index.d.ts +1 -1
  57. package/dist_ts/proxies/smart-proxy/utils/index.js +3 -4
  58. package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.d.ts +49 -0
  59. package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.js +108 -0
  60. package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.d.ts +57 -0
  61. package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.js +89 -0
  62. package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.d.ts +17 -0
  63. package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.js +32 -0
  64. package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.d.ts +68 -0
  65. package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.js +117 -0
  66. package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.d.ts +17 -0
  67. package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.js +27 -0
  68. package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.d.ts +63 -0
  69. package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.js +105 -0
  70. package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.d.ts +83 -0
  71. package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.js +126 -0
  72. package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.d.ts +47 -0
  73. package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.js +66 -0
  74. package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.d.ts +70 -0
  75. package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.js +287 -0
  76. package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.d.ts +46 -0
  77. package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.js +67 -0
  78. package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +4 -457
  79. package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +6 -950
  80. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +2 -2
  81. package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +67 -1
  82. package/dist_ts/proxies/smart-proxy/utils/route-validator.js +251 -3
  83. package/dist_ts/routing/index.d.ts +1 -1
  84. package/dist_ts/routing/index.js +3 -3
  85. package/dist_ts/routing/models/http-types.d.ts +119 -4
  86. package/dist_ts/routing/models/http-types.js +93 -5
  87. package/npmextra.json +12 -6
  88. package/package.json +34 -24
  89. package/readme.hints.md +184 -1
  90. package/readme.md +580 -266
  91. package/ts/00_commitinfo_data.ts +1 -1
  92. package/ts/core/utils/shared-security-manager.ts +98 -13
  93. package/ts/index.ts +4 -12
  94. package/ts/protocols/common/fragment-handler.ts +4 -0
  95. package/ts/proxies/index.ts +1 -9
  96. package/ts/proxies/nftables-proxy/index.ts +1 -0
  97. package/ts/proxies/nftables-proxy/nftables-proxy.ts +116 -290
  98. package/ts/proxies/nftables-proxy/utils/index.ts +38 -0
  99. package/ts/proxies/nftables-proxy/utils/nft-command-executor.ts +162 -0
  100. package/ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.ts +125 -0
  101. package/ts/proxies/nftables-proxy/utils/nft-rule-validator.ts +156 -0
  102. package/ts/proxies/smart-proxy/index.ts +6 -13
  103. package/ts/proxies/smart-proxy/models/interfaces.ts +6 -5
  104. package/ts/proxies/smart-proxy/route-preprocessor.ts +122 -0
  105. package/ts/proxies/smart-proxy/rust-binary-locator.ts +112 -0
  106. package/ts/proxies/smart-proxy/rust-metrics-adapter.ts +161 -0
  107. package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +310 -0
  108. package/ts/proxies/smart-proxy/smart-proxy.ts +282 -800
  109. package/ts/proxies/smart-proxy/socket-handler-server.ts +279 -0
  110. package/ts/proxies/smart-proxy/utils/index.ts +3 -5
  111. package/ts/proxies/smart-proxy/utils/route-helpers/api-helpers.ts +144 -0
  112. package/ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.ts +124 -0
  113. package/ts/proxies/smart-proxy/utils/route-helpers/http-helpers.ts +40 -0
  114. package/ts/proxies/smart-proxy/utils/route-helpers/https-helpers.ts +163 -0
  115. package/ts/proxies/smart-proxy/utils/route-helpers/index.ts +62 -0
  116. package/ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.ts +154 -0
  117. package/ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.ts +202 -0
  118. package/ts/proxies/smart-proxy/utils/route-helpers/security-helpers.ts +96 -0
  119. package/ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.ts +337 -0
  120. package/ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.ts +98 -0
  121. package/ts/proxies/smart-proxy/utils/route-helpers.ts +5 -1302
  122. package/ts/proxies/smart-proxy/utils/route-utils.ts +1 -1
  123. package/ts/proxies/smart-proxy/utils/route-validator.ts +274 -4
  124. package/ts/routing/index.ts +2 -2
  125. package/ts/routing/models/http-types.ts +147 -4
  126. package/ts/proxies/http-proxy/certificate-manager.ts +0 -244
  127. package/ts/proxies/http-proxy/connection-pool.ts +0 -228
  128. package/ts/proxies/http-proxy/context-creator.ts +0 -145
  129. package/ts/proxies/http-proxy/function-cache.ts +0 -279
  130. package/ts/proxies/http-proxy/handlers/index.ts +0 -5
  131. package/ts/proxies/http-proxy/http-proxy.ts +0 -675
  132. package/ts/proxies/http-proxy/http-request-handler.ts +0 -331
  133. package/ts/proxies/http-proxy/http2-request-handler.ts +0 -255
  134. package/ts/proxies/http-proxy/index.ts +0 -13
  135. package/ts/proxies/http-proxy/models/http-types.ts +0 -148
  136. package/ts/proxies/http-proxy/models/index.ts +0 -5
  137. package/ts/proxies/http-proxy/models/types.ts +0 -125
  138. package/ts/proxies/http-proxy/request-handler.ts +0 -878
  139. package/ts/proxies/http-proxy/security-manager.ts +0 -433
  140. package/ts/proxies/http-proxy/websocket-handler.ts +0 -581
  141. package/ts/proxies/smart-proxy/acme-state-manager.ts +0 -112
  142. package/ts/proxies/smart-proxy/cert-store.ts +0 -92
  143. package/ts/proxies/smart-proxy/certificate-manager.ts +0 -894
  144. package/ts/proxies/smart-proxy/connection-manager.ts +0 -796
  145. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +0 -187
  146. package/ts/proxies/smart-proxy/metrics-collector.ts +0 -453
  147. package/ts/proxies/smart-proxy/nftables-manager.ts +0 -271
  148. package/ts/proxies/smart-proxy/port-manager.ts +0 -358
  149. package/ts/proxies/smart-proxy/route-connection-handler.ts +0 -1640
  150. package/ts/proxies/smart-proxy/route-orchestrator.ts +0 -297
  151. package/ts/proxies/smart-proxy/security-manager.ts +0 -257
  152. package/ts/proxies/smart-proxy/throughput-tracker.ts +0 -138
  153. package/ts/proxies/smart-proxy/timeout-manager.ts +0 -196
  154. package/ts/proxies/smart-proxy/tls-manager.ts +0 -207
  155. package/ts/proxies/smart-proxy/utils/route-validators.ts +0 -283
@@ -0,0 +1,104 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { logger } from '../../core/utils/logger.js';
3
+ /**
4
+ * Locates the RustProxy binary using a priority-ordered search strategy:
5
+ * 1. SMARTPROXY_RUST_BINARY environment variable
6
+ * 2. Platform-specific optional npm package
7
+ * 3. Local development build at ./rust/target/release/rustproxy
8
+ * 4. System PATH
9
+ */
10
+ export class RustBinaryLocator {
11
+ constructor() {
12
+ this.cachedPath = null;
13
+ }
14
+ /**
15
+ * Find the RustProxy binary path.
16
+ * Returns null if no binary is available.
17
+ */
18
+ async findBinary() {
19
+ if (this.cachedPath !== null) {
20
+ return this.cachedPath;
21
+ }
22
+ const path = await this.searchBinary();
23
+ this.cachedPath = path;
24
+ return path;
25
+ }
26
+ /**
27
+ * Clear the cached binary path (e.g., after a failed launch).
28
+ */
29
+ clearCache() {
30
+ this.cachedPath = null;
31
+ }
32
+ async searchBinary() {
33
+ // 1. Environment variable override
34
+ const envPath = process.env.SMARTPROXY_RUST_BINARY;
35
+ if (envPath) {
36
+ if (await this.isExecutable(envPath)) {
37
+ logger.log('info', `RustProxy binary found via SMARTPROXY_RUST_BINARY: ${envPath}`, { component: 'rust-locator' });
38
+ return envPath;
39
+ }
40
+ logger.log('warn', `SMARTPROXY_RUST_BINARY set but not executable: ${envPath}`, { component: 'rust-locator' });
41
+ }
42
+ // 2. Platform-specific optional npm package
43
+ const platformBinary = await this.findPlatformPackageBinary();
44
+ if (platformBinary) {
45
+ logger.log('info', `RustProxy binary found in platform package: ${platformBinary}`, { component: 'rust-locator' });
46
+ return platformBinary;
47
+ }
48
+ // 3. Local development build
49
+ const localPaths = [
50
+ plugins.path.resolve(process.cwd(), 'rust/target/release/rustproxy'),
51
+ plugins.path.resolve(process.cwd(), 'rust/target/debug/rustproxy'),
52
+ ];
53
+ for (const localPath of localPaths) {
54
+ if (await this.isExecutable(localPath)) {
55
+ logger.log('info', `RustProxy binary found at local path: ${localPath}`, { component: 'rust-locator' });
56
+ return localPath;
57
+ }
58
+ }
59
+ // 4. System PATH
60
+ const systemPath = await this.findInPath('rustproxy');
61
+ if (systemPath) {
62
+ logger.log('info', `RustProxy binary found in system PATH: ${systemPath}`, { component: 'rust-locator' });
63
+ return systemPath;
64
+ }
65
+ logger.log('error', 'No RustProxy binary found. Set SMARTPROXY_RUST_BINARY, install the platform package, or build with: cd rust && cargo build --release', { component: 'rust-locator' });
66
+ return null;
67
+ }
68
+ async findPlatformPackageBinary() {
69
+ const platform = process.platform;
70
+ const arch = process.arch;
71
+ const packageName = `@push.rocks/smartproxy-${platform}-${arch}`;
72
+ try {
73
+ // Try to resolve the platform-specific package
74
+ const packagePath = require.resolve(`${packageName}/rustproxy`);
75
+ if (await this.isExecutable(packagePath)) {
76
+ return packagePath;
77
+ }
78
+ }
79
+ catch {
80
+ // Package not installed - expected for development
81
+ }
82
+ return null;
83
+ }
84
+ async isExecutable(filePath) {
85
+ try {
86
+ await plugins.fs.promises.access(filePath, plugins.fs.constants.X_OK);
87
+ return true;
88
+ }
89
+ catch {
90
+ return false;
91
+ }
92
+ }
93
+ async findInPath(binaryName) {
94
+ const pathDirs = (process.env.PATH || '').split(plugins.path.delimiter);
95
+ for (const dir of pathDirs) {
96
+ const fullPath = plugins.path.join(dir, binaryName);
97
+ if (await this.isExecutable(fullPath)) {
98
+ return fullPath;
99
+ }
100
+ }
101
+ return null;
102
+ }
103
+ }
104
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVzdC1iaW5hcnktbG9jYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvcnVzdC1iaW5hcnktbG9jYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUVwRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLE9BQU8saUJBQWlCO0lBQTlCO1FBQ1UsZUFBVSxHQUFrQixJQUFJLENBQUM7SUFvRzNDLENBQUM7SUFsR0M7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLFVBQVU7UUFDckIsSUFBSSxJQUFJLENBQUMsVUFBVSxLQUFLLElBQUksRUFBRSxDQUFDO1lBQzdCLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQztRQUN6QixDQUFDO1FBRUQsTUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDdkMsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7UUFDdkIsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxVQUFVO1FBQ2YsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7SUFDekIsQ0FBQztJQUVPLEtBQUssQ0FBQyxZQUFZO1FBQ3hCLG1DQUFtQztRQUNuQyxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLHNCQUFzQixDQUFDO1FBQ25ELElBQUksT0FBTyxFQUFFLENBQUM7WUFDWixJQUFJLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUNyQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxzREFBc0QsT0FBTyxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsY0FBYyxFQUFFLENBQUMsQ0FBQztnQkFDbkgsT0FBTyxPQUFPLENB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
@@ -0,0 +1,74 @@
1
+ import type { IMetrics, IThroughputData, IThroughputHistoryPoint } from './models/metrics-types.js';
2
+ import type { RustProxyBridge } from './rust-proxy-bridge.js';
3
+ /**
4
+ * Adapts Rust JSON metrics to the IMetrics interface.
5
+ *
6
+ * Polls the Rust binary periodically via the bridge and caches the result.
7
+ * All IMetrics getters read from the cache synchronously.
8
+ *
9
+ * Rust Metrics JSON fields (camelCase via serde):
10
+ * activeConnections, totalConnections, bytesIn, bytesOut,
11
+ * throughputInBytesPerSec, throughputOutBytesPerSec,
12
+ * routes: { [routeName]: { activeConnections, totalConnections, bytesIn, bytesOut, ... } }
13
+ */
14
+ export declare class RustMetricsAdapter implements IMetrics {
15
+ private bridge;
16
+ private cache;
17
+ private pollTimer;
18
+ private pollIntervalMs;
19
+ constructor(bridge: RustProxyBridge, pollIntervalMs?: number);
20
+ /**
21
+ * Poll Rust for metrics once. Can be awaited to ensure cache is fresh.
22
+ */
23
+ poll(): Promise<void>;
24
+ startPolling(): void;
25
+ stopPolling(): void;
26
+ connections: {
27
+ active: () => number;
28
+ total: () => number;
29
+ byRoute: () => Map<string, number>;
30
+ byIP: () => Map<string, number>;
31
+ topIPs: (_limit?: number) => Array<{
32
+ ip: string;
33
+ count: number;
34
+ }>;
35
+ };
36
+ throughput: {
37
+ instant: () => IThroughputData;
38
+ recent: () => IThroughputData;
39
+ average: () => IThroughputData;
40
+ custom: (_seconds: number) => IThroughputData;
41
+ history: (_seconds: number) => Array<IThroughputHistoryPoint>;
42
+ byRoute: (_windowSeconds?: number) => Map<string, IThroughputData>;
43
+ byIP: (_windowSeconds?: number) => Map<string, IThroughputData>;
44
+ };
45
+ requests: {
46
+ perSecond: () => number;
47
+ perMinute: () => number;
48
+ total: () => number;
49
+ };
50
+ totals: {
51
+ bytesIn: () => number;
52
+ bytesOut: () => number;
53
+ connections: () => number;
54
+ };
55
+ percentiles: {
56
+ connectionDuration: () => {
57
+ p50: number;
58
+ p95: number;
59
+ p99: number;
60
+ };
61
+ bytesTransferred: () => {
62
+ in: {
63
+ p50: number;
64
+ p95: number;
65
+ p99: number;
66
+ };
67
+ out: {
68
+ p50: number;
69
+ p95: number;
70
+ p99: number;
71
+ };
72
+ };
73
+ };
74
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Adapts Rust JSON metrics to the IMetrics interface.
3
+ *
4
+ * Polls the Rust binary periodically via the bridge and caches the result.
5
+ * All IMetrics getters read from the cache synchronously.
6
+ *
7
+ * Rust Metrics JSON fields (camelCase via serde):
8
+ * activeConnections, totalConnections, bytesIn, bytesOut,
9
+ * throughputInBytesPerSec, throughputOutBytesPerSec,
10
+ * routes: { [routeName]: { activeConnections, totalConnections, bytesIn, bytesOut, ... } }
11
+ */
12
+ export class RustMetricsAdapter {
13
+ constructor(bridge, pollIntervalMs = 1000) {
14
+ this.cache = null;
15
+ this.pollTimer = null;
16
+ // --- IMetrics implementation ---
17
+ this.connections = {
18
+ active: () => {
19
+ return this.cache?.activeConnections ?? 0;
20
+ },
21
+ total: () => {
22
+ return this.cache?.totalConnections ?? 0;
23
+ },
24
+ byRoute: () => {
25
+ const result = new Map();
26
+ if (this.cache?.routes) {
27
+ for (const [name, rm] of Object.entries(this.cache.routes)) {
28
+ result.set(name, rm.activeConnections ?? 0);
29
+ }
30
+ }
31
+ return result;
32
+ },
33
+ byIP: () => {
34
+ // Per-IP tracking not yet available from Rust
35
+ return new Map();
36
+ },
37
+ topIPs: (_limit) => {
38
+ // Per-IP tracking not yet available from Rust
39
+ return [];
40
+ },
41
+ };
42
+ this.throughput = {
43
+ instant: () => {
44
+ return {
45
+ in: this.cache?.throughputInBytesPerSec ?? 0,
46
+ out: this.cache?.throughputOutBytesPerSec ?? 0,
47
+ };
48
+ },
49
+ recent: () => {
50
+ return this.throughput.instant();
51
+ },
52
+ average: () => {
53
+ return this.throughput.instant();
54
+ },
55
+ custom: (_seconds) => {
56
+ return this.throughput.instant();
57
+ },
58
+ history: (_seconds) => {
59
+ // Throughput history not yet available from Rust
60
+ return [];
61
+ },
62
+ byRoute: (_windowSeconds) => {
63
+ const result = new Map();
64
+ if (this.cache?.routes) {
65
+ for (const [name, rm] of Object.entries(this.cache.routes)) {
66
+ result.set(name, {
67
+ in: rm.throughputInBytesPerSec ?? 0,
68
+ out: rm.throughputOutBytesPerSec ?? 0,
69
+ });
70
+ }
71
+ }
72
+ return result;
73
+ },
74
+ byIP: (_windowSeconds) => {
75
+ return new Map();
76
+ },
77
+ };
78
+ this.requests = {
79
+ perSecond: () => {
80
+ // Rust tracks connections, not HTTP requests (TCP-level proxy)
81
+ return 0;
82
+ },
83
+ perMinute: () => {
84
+ return 0;
85
+ },
86
+ total: () => {
87
+ // Use total connections as a proxy for total requests
88
+ return this.cache?.totalConnections ?? 0;
89
+ },
90
+ };
91
+ this.totals = {
92
+ bytesIn: () => {
93
+ return this.cache?.bytesIn ?? 0;
94
+ },
95
+ bytesOut: () => {
96
+ return this.cache?.bytesOut ?? 0;
97
+ },
98
+ connections: () => {
99
+ return this.cache?.totalConnections ?? 0;
100
+ },
101
+ };
102
+ this.percentiles = {
103
+ connectionDuration: () => {
104
+ return { p50: 0, p95: 0, p99: 0 };
105
+ },
106
+ bytesTransferred: () => {
107
+ return {
108
+ in: { p50: 0, p95: 0, p99: 0 },
109
+ out: { p50: 0, p95: 0, p99: 0 },
110
+ };
111
+ },
112
+ };
113
+ this.bridge = bridge;
114
+ this.pollIntervalMs = pollIntervalMs;
115
+ }
116
+ /**
117
+ * Poll Rust for metrics once. Can be awaited to ensure cache is fresh.
118
+ */
119
+ async poll() {
120
+ try {
121
+ this.cache = await this.bridge.getMetrics();
122
+ }
123
+ catch {
124
+ // Ignore poll errors (bridge may be shutting down)
125
+ }
126
+ }
127
+ startPolling() {
128
+ if (this.pollTimer)
129
+ return;
130
+ // Immediate first poll so cache is populated ASAP
131
+ this.poll();
132
+ this.pollTimer = setInterval(() => {
133
+ this.poll();
134
+ }, this.pollIntervalMs);
135
+ if (this.pollTimer.unref) {
136
+ this.pollTimer.unref();
137
+ }
138
+ }
139
+ stopPolling() {
140
+ if (this.pollTimer) {
141
+ clearInterval(this.pollTimer);
142
+ this.pollTimer = null;
143
+ }
144
+ }
145
+ }
146
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVzdC1tZXRyaWNzLWFkYXB0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L3J1c3QtbWV0cmljcy1hZGFwdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUdBOzs7Ozs7Ozs7O0dBVUc7QUFDSCxNQUFNLE9BQU8sa0JBQWtCO0lBTTdCLFlBQVksTUFBdUIsRUFBRSxjQUFjLEdBQUcsSUFBSTtRQUpsRCxVQUFLLEdBQVEsSUFBSSxDQUFDO1FBQ2xCLGNBQVMsR0FBMEMsSUFBSSxDQUFDO1FBc0NoRSxrQ0FBa0M7UUFFM0IsZ0JBQVcsR0FBRztZQUNuQixNQUFNLEVBQUUsR0FBVyxFQUFFO2dCQUNuQixPQUFPLElBQUksQ0FBQyxLQUFLLEVBQUUsaUJBQWlCLElBQUksQ0FBQyxDQUFDO1lBQzVDLENBQUM7WUFDRCxLQUFLLEVBQUUsR0FBVyxFQUFFO2dCQUNsQixPQUFPLElBQUksQ0FBQyxLQUFLLEVBQUUsZ0JBQWdCLElBQUksQ0FBQyxDQUFDO1lBQzNDLENBQUM7WUFDRCxPQUFPLEVBQUUsR0FBd0IsRUFBRTtnQkFDakMsTUFBTSxNQUFNLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUM7Z0JBQ3pDLElBQUksSUFBSSxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsQ0FBQztvQkFDdkIsS0FBSyxNQUFNLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO3dCQUMzRCxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRyxFQUFVLENBQUMsaUJBQWlCLElBQUksQ0FBQyxDQUFDLENBQUM7b0JBQ3ZELENBQUM7Z0JBQ0gsQ0FBQztnQkFDRCxPQUFPLE1BQU0sQ0FBQztZQUNoQixDQUFDO1lBQ0QsSUFBSSxFQUFFLEdBQXdCLEVBQUU7Z0JBQzlCLDhDQUE4QztnQkFDOUMsT0FBTyxJQUFJLEdBQUcsRUFBRSxDQUFDO1lBQ25CLENBQUM7WUFDRCxNQUFNLEVBQUUsQ0FBQyxNQUFlLEVBQXdDLEVBQUU7Z0JBQ2hFLDhDQUE4QztnQkFDOUMsT0FBTyxFQUFFLENBQUM7WUFDWixDQUFDO1NBQ0YsQ0FBQztRQUVLLGVBQVUsR0FBRztZQUNsQixPQUFPLEVBQUUsR0FBb0IsRUFBRTtnQkFDN0IsT0FBTztvQkFDTCxFQUFFLEVBQUUsSUFBSSxDQUFDLEtBQUssRUFBRSx1QkFBdUIsSUFBSSxDQUFDO29CQUM1QyxHQUFHLEVBQUUsSUFBSSxDQUFDLEtBQUssRUFBRSx3QkFBd0IsSUFBSSxDQUFDO2lCQUMvQyxDQUFDO1lBQ0osQ0FBQztZQUNELE1BQU0sRUFBRSxHQUFvQixFQUFFO2dCQUM1QixPQUFPLElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDbkMsQ0FBQztZQUNELE9BQU8sRUFBRSxHQUFvQixFQUFFO2dCQUM3QixPQUFPLElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDbkMsQ0FBQztZQUNELE1BQU0sRUFBRSxDQUFDLFFBQWdCLEVBQW1CLEVBQUU7Z0JBQzVDLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNuQyxDQUFDO1lBQ0QsT0FBTyxFQUFFLENBQUMsUUFBZ0IsRUFBa0MsRUFBRTtnQkFDNUQsaURBQWlEO2dCQUNqRCxPQUFPLEVBQUUsQ0FBQztZQUNaLENBQUM7WUFDRCxPQUFPLEVBQUUsQ0FBQyxjQUF1QixFQUFnQyxFQUFFO2dCQUNqRSxNQUFNLE1BQU0sR0FBRyxJQUFJLEdBQUcsRUFBMkIsQ0FBQztnQkFDbEQsSUFBSSxJQUFJLENBQUMsS0FBSyxFQUFFLE1BQU0sRUFBRSxDQUFDO29CQUN2QixLQUFLLE1BQU0sQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7d0JBQzNELE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFOzRCQUNmLEVBQUUsRUFBRyxFQUFVLENBQUMsdUJBQXVCLElBQUksQ0FBQzs0QkFDNUMsR0FBRyxFQUFHLEVBQVUsQ0FBQyx3QkFBd0IsSUFBSSxDQUFDO3lCQUMvQyxDQUFDLENBQUM7b0JBQ0wsQ0FBQztnQkFDSCxDQUFDO2dCQUNELE9BQU8sTUFBTSxDQUFDO1lBQ2hCLENBQUM7WUFDRCxJQUFJLEVBQUUsQ0FBQyxjQUF1QixFQUFnQyxFQUFFO2dCQUM5RCxPQUFPLElBQUksR0FBRyxFQUFFLENBQUM7WUFDbkIsQ0FBQztTQUNGLENBQUM7UUFFSyxhQUFRLEdBQUc7WUFDaEIsU0FBUyxFQUFFLEdBQVcsRUFBRTtnQkFDdEIsK0RBQStEO2dCQUMvRCxPQUFPLENBQUMsQ0FBQztZQUNYLENBQUM7WUFDRCxTQUFTLEVBQUUsR0FBVyxFQUFFO2dCQUN0QixPQUFPLENBQUMsQ0FBQztZQUNYLENBQUM7WUFDRCxLQUFLLEVBQUUsR0FBVyxFQUFFO2dCQUNsQixzREFBc0Q7Z0JBQ3RELE9BQU8sSUFBSSxDQUFDLEtBQUssRUFBRSxnQkFBZ0IsSUFBSSxDQUFDLENBQUM7WUFDM0MsQ0FBQztTQUNGLENBQUM7UUFFSyxXQUFNLEdBQUc7WUFDZCxPQUFPLEVBQUUsR0FBVyxFQUFFO2dCQUNwQixPQUFPLElBQUksQ0FBQyxLQUFLLEVBQUUsT0FBTyxJQUFJLENBQUMsQ0FBQztZQUNsQyxDQUFDO1lBQ0QsUUFBUSxFQUFFLEdBQVcsRUFBRTtnQkFDckIsT0FBTyxJQUFJLENBQUMsS0FBSyxFQUFFLFFBQVEsSUFBSSxDQUFDLENBQUM7WUFDbkMsQ0FBQztZQUNELFdBQVcsRUFBRSxHQUFXLEVBQUU7Z0JBQ3hCLE9BQU8sSUFBSSxDQUFDLEtBQUssRUFBRSxnQkFBZ0IsSUFBSSxDQUFDLENBQUM7WUFDM0MsQ0FBQztTQUNGLENBQUM7UUFFSyxnQkFBVyxHQUFHO1lBQ25CLGtCQUFrQixFQUFFLEdBQThDLEVBQUU7Z0JBQ2xFLE9BQU8sRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQ3BDLENBQUM7WUFDRCxnQkFBZ0IsRUFBRSxHQUdoQixFQUFFO2dCQUNGLE9BQU87b0JBQ0wsRUFBRSxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUU7b0JBQzlCLEdBQUcsRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFO2lCQUNoQyxDQUFDO1lBQ0osQ0FBQztTQUNGLENBQUM7UUExSUEsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLGNBQWMsR0FBRyxjQUFjLENBQUM7SUFDdkMsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLElBQUk7UUFDZixJQUFJLENBQUM7WUFDSCxJQUFJLENBQUMsS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUM5QyxDQUFDO1FBQUMsTUFBTSxDQUFDO1lBQ1AsbURBQW1EO1FBQ3JELENBQUM7SUFDSCxDQUFDO0lBRU0sWUFBWTtRQUNqQixJQUFJLElBQUksQ0FBQyxTQUFTO1lBQUUsT0FBTztRQUMzQixrREFBa0Q7UUFDbEQsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ1osSUFBSSxDQUFDLFNBQVMsR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFO1lBQ2hDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNkLENBQUMsRUFBRSxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDeEIsSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ3pCLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDekIsQ0FBQztJQUNILENBQUM7SUFFTSxXQUFXO1FBQ2hCLElBQUksSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQ25CLGFBQWEsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDOUIsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUM7UUFDeEIsQ0FBQztJQUNILENBQUM7Q0EyR0YifQ==
@@ -0,0 +1,49 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import type { IRouteConfig } from './models/route-types.js';
3
+ /**
4
+ * Bridge between TypeScript SmartProxy and the Rust binary.
5
+ * Communicates via JSON-over-stdin/stdout IPC protocol.
6
+ */
7
+ export declare class RustProxyBridge extends plugins.EventEmitter {
8
+ private locator;
9
+ private process;
10
+ private readline;
11
+ private pendingRequests;
12
+ private requestCounter;
13
+ private isRunning;
14
+ private binaryPath;
15
+ private readonly requestTimeoutMs;
16
+ /**
17
+ * Spawn the Rust binary in management mode.
18
+ * Returns true if the binary was found and spawned successfully.
19
+ */
20
+ spawn(): Promise<boolean>;
21
+ /**
22
+ * Send a management command to the Rust process and wait for the response.
23
+ */
24
+ sendCommand(method: string, params?: Record<string, any>): Promise<any>;
25
+ startProxy(config: any): Promise<void>;
26
+ stopProxy(): Promise<void>;
27
+ updateRoutes(routes: IRouteConfig[]): Promise<void>;
28
+ getMetrics(): Promise<any>;
29
+ getStatistics(): Promise<any>;
30
+ provisionCertificate(routeName: string): Promise<void>;
31
+ renewCertificate(routeName: string): Promise<void>;
32
+ getCertificateStatus(routeName: string): Promise<any>;
33
+ getListeningPorts(): Promise<number[]>;
34
+ getNftablesStatus(): Promise<any>;
35
+ setSocketHandlerRelay(socketPath: string): Promise<void>;
36
+ addListeningPort(port: number): Promise<void>;
37
+ removeListeningPort(port: number): Promise<void>;
38
+ loadCertificate(domain: string, cert: string, key: string, ca?: string): Promise<void>;
39
+ /**
40
+ * Kill the Rust process and clean up all stdio streams.
41
+ */
42
+ kill(): void;
43
+ /**
44
+ * Whether the bridge is currently running.
45
+ */
46
+ get running(): boolean;
47
+ private handleLine;
48
+ private cleanup;
49
+ }
@@ -0,0 +1,259 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { logger } from '../../core/utils/logger.js';
3
+ import { RustBinaryLocator } from './rust-binary-locator.js';
4
+ import { ChildProcess, spawn } from 'child_process';
5
+ import { createInterface, Interface as ReadlineInterface } from 'readline';
6
+ /**
7
+ * Bridge between TypeScript SmartProxy and the Rust binary.
8
+ * Communicates via JSON-over-stdin/stdout IPC protocol.
9
+ */
10
+ export class RustProxyBridge extends plugins.EventEmitter {
11
+ constructor() {
12
+ super(...arguments);
13
+ this.locator = new RustBinaryLocator();
14
+ this.process = null;
15
+ this.readline = null;
16
+ this.pendingRequests = new Map();
17
+ this.requestCounter = 0;
18
+ this.isRunning = false;
19
+ this.binaryPath = null;
20
+ this.requestTimeoutMs = 30000;
21
+ }
22
+ /**
23
+ * Spawn the Rust binary in management mode.
24
+ * Returns true if the binary was found and spawned successfully.
25
+ */
26
+ async spawn() {
27
+ this.binaryPath = await this.locator.findBinary();
28
+ if (!this.binaryPath) {
29
+ return false;
30
+ }
31
+ return new Promise((resolve) => {
32
+ try {
33
+ this.process = spawn(this.binaryPath, ['--management'], {
34
+ stdio: ['pipe', 'pipe', 'pipe'],
35
+ env: { ...process.env },
36
+ });
37
+ // Handle stderr (logging from Rust goes here)
38
+ const stderrHandler = (data) => {
39
+ const lines = data.toString().split('\n').filter(l => l.trim());
40
+ for (const line of lines) {
41
+ logger.log('debug', `[rustproxy] ${line}`, { component: 'rust-bridge' });
42
+ }
43
+ };
44
+ this.process.stderr?.on('data', stderrHandler);
45
+ // Handle stdout (JSON IPC)
46
+ this.readline = createInterface({ input: this.process.stdout });
47
+ this.readline.on('line', (line) => {
48
+ this.handleLine(line.trim());
49
+ });
50
+ // Handle process exit
51
+ this.process.on('exit', (code, signal) => {
52
+ logger.log('info', `RustProxy process exited (code=${code}, signal=${signal})`, { component: 'rust-bridge' });
53
+ this.cleanup();
54
+ this.emit('exit', code, signal);
55
+ });
56
+ this.process.on('error', (err) => {
57
+ logger.log('error', `RustProxy process error: ${err.message}`, { component: 'rust-bridge' });
58
+ this.cleanup();
59
+ resolve(false);
60
+ });
61
+ // Wait for the 'ready' event from Rust
62
+ const readyTimeout = setTimeout(() => {
63
+ logger.log('error', 'RustProxy did not send ready event within 10s', { component: 'rust-bridge' });
64
+ this.kill();
65
+ resolve(false);
66
+ }, 10000);
67
+ this.once('management:ready', () => {
68
+ clearTimeout(readyTimeout);
69
+ this.isRunning = true;
70
+ logger.log('info', 'RustProxy bridge connected', { component: 'rust-bridge' });
71
+ resolve(true);
72
+ });
73
+ }
74
+ catch (err) {
75
+ logger.log('error', `Failed to spawn RustProxy: ${err.message}`, { component: 'rust-bridge' });
76
+ resolve(false);
77
+ }
78
+ });
79
+ }
80
+ /**
81
+ * Send a management command to the Rust process and wait for the response.
82
+ */
83
+ async sendCommand(method, params = {}) {
84
+ if (!this.process || !this.isRunning) {
85
+ throw new Error('RustProxy bridge is not running');
86
+ }
87
+ const id = `req_${++this.requestCounter}`;
88
+ const request = { id, method, params };
89
+ return new Promise((resolve, reject) => {
90
+ const timer = setTimeout(() => {
91
+ this.pendingRequests.delete(id);
92
+ reject(new Error(`RustProxy command '${method}' timed out after ${this.requestTimeoutMs}ms`));
93
+ }, this.requestTimeoutMs);
94
+ this.pendingRequests.set(id, { resolve, reject, timer });
95
+ const json = JSON.stringify(request) + '\n';
96
+ this.process.stdin.write(json, (err) => {
97
+ if (err) {
98
+ clearTimeout(timer);
99
+ this.pendingRequests.delete(id);
100
+ reject(new Error(`Failed to write to RustProxy stdin: ${err.message}`));
101
+ }
102
+ });
103
+ });
104
+ }
105
+ // Convenience methods for each management command
106
+ async startProxy(config) {
107
+ await this.sendCommand('start', { config });
108
+ }
109
+ async stopProxy() {
110
+ await this.sendCommand('stop');
111
+ }
112
+ async updateRoutes(routes) {
113
+ await this.sendCommand('updateRoutes', { routes });
114
+ }
115
+ async getMetrics() {
116
+ return this.sendCommand('getMetrics');
117
+ }
118
+ async getStatistics() {
119
+ return this.sendCommand('getStatistics');
120
+ }
121
+ async provisionCertificate(routeName) {
122
+ await this.sendCommand('provisionCertificate', { routeName });
123
+ }
124
+ async renewCertificate(routeName) {
125
+ await this.sendCommand('renewCertificate', { routeName });
126
+ }
127
+ async getCertificateStatus(routeName) {
128
+ return this.sendCommand('getCertificateStatus', { routeName });
129
+ }
130
+ async getListeningPorts() {
131
+ const result = await this.sendCommand('getListeningPorts');
132
+ return result?.ports ?? [];
133
+ }
134
+ async getNftablesStatus() {
135
+ return this.sendCommand('getNftablesStatus');
136
+ }
137
+ async setSocketHandlerRelay(socketPath) {
138
+ await this.sendCommand('setSocketHandlerRelay', { socketPath });
139
+ }
140
+ async addListeningPort(port) {
141
+ await this.sendCommand('addListeningPort', { port });
142
+ }
143
+ async removeListeningPort(port) {
144
+ await this.sendCommand('removeListeningPort', { port });
145
+ }
146
+ async loadCertificate(domain, cert, key, ca) {
147
+ await this.sendCommand('loadCertificate', { domain, cert, key, ca });
148
+ }
149
+ /**
150
+ * Kill the Rust process and clean up all stdio streams.
151
+ */
152
+ kill() {
153
+ if (this.process) {
154
+ const proc = this.process;
155
+ this.process = null;
156
+ this.isRunning = false;
157
+ // Close readline (reads from stdout)
158
+ if (this.readline) {
159
+ this.readline.close();
160
+ this.readline = null;
161
+ }
162
+ // Reject pending requests
163
+ for (const [, pending] of this.pendingRequests) {
164
+ clearTimeout(pending.timer);
165
+ pending.reject(new Error('RustProxy process killed'));
166
+ }
167
+ this.pendingRequests.clear();
168
+ // Remove all listeners so nothing keeps references
169
+ proc.removeAllListeners();
170
+ proc.stdout?.removeAllListeners();
171
+ proc.stderr?.removeAllListeners();
172
+ proc.stdin?.removeAllListeners();
173
+ // Kill the process
174
+ try {
175
+ proc.kill('SIGTERM');
176
+ }
177
+ catch { /* already dead */ }
178
+ // Destroy all stdio pipes to free handles
179
+ try {
180
+ proc.stdin?.destroy();
181
+ }
182
+ catch { /* ignore */ }
183
+ try {
184
+ proc.stdout?.destroy();
185
+ }
186
+ catch { /* ignore */ }
187
+ try {
188
+ proc.stderr?.destroy();
189
+ }
190
+ catch { /* ignore */ }
191
+ // Unref process so Node doesn't wait for it
192
+ try {
193
+ proc.unref();
194
+ }
195
+ catch { /* ignore */ }
196
+ // Force kill after 5 seconds
197
+ setTimeout(() => {
198
+ try {
199
+ proc.kill('SIGKILL');
200
+ }
201
+ catch { /* already dead */ }
202
+ }, 5000).unref();
203
+ }
204
+ }
205
+ /**
206
+ * Whether the bridge is currently running.
207
+ */
208
+ get running() {
209
+ return this.isRunning;
210
+ }
211
+ handleLine(line) {
212
+ if (!line)
213
+ return;
214
+ let parsed;
215
+ try {
216
+ parsed = JSON.parse(line);
217
+ }
218
+ catch {
219
+ logger.log('warn', `Non-JSON output from RustProxy: ${line}`, { component: 'rust-bridge' });
220
+ return;
221
+ }
222
+ // Check if it's an event (has 'event' field)
223
+ if ('event' in parsed) {
224
+ const event = parsed;
225
+ this.emit(`management:${event.event}`, event.data);
226
+ return;
227
+ }
228
+ // Otherwise it's a response (has 'id' field)
229
+ if ('id' in parsed) {
230
+ const response = parsed;
231
+ const pending = this.pendingRequests.get(response.id);
232
+ if (pending) {
233
+ clearTimeout(pending.timer);
234
+ this.pendingRequests.delete(response.id);
235
+ if (response.success) {
236
+ pending.resolve(response.result);
237
+ }
238
+ else {
239
+ pending.reject(new Error(response.error || 'Unknown error from RustProxy'));
240
+ }
241
+ }
242
+ }
243
+ }
244
+ cleanup() {
245
+ this.isRunning = false;
246
+ this.process = null;
247
+ if (this.readline) {
248
+ this.readline.close();
249
+ this.readline = null;
250
+ }
251
+ // Reject all pending requests
252
+ for (const [id, pending] of this.pendingRequests) {
253
+ clearTimeout(pending.timer);
254
+ pending.reject(new Error('RustProxy process exited'));
255
+ }
256
+ this.pendingRequests.clear();
257
+ }
258
+ }
259
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVzdC1wcm94eS1icmlkZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L3J1c3QtcHJveHktYnJpZGdlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3BELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBRTdELE9BQU8sRUFBRSxZQUFZLEVBQUUsS0FBSyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3BELE9BQU8sRUFBRSxlQUFlLEVBQUUsU0FBUyxJQUFJLGlCQUFpQixFQUFFLE1BQU0sVUFBVSxDQUFDO0FBNkIzRTs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sZUFBZ0IsU0FBUSxPQUFPLENBQUMsWUFBWTtJQUF6RDs7UUFDVSxZQUFPLEdBQUcsSUFBSSxpQkFBaUIsRUFBRSxDQUFDO1FBQ2xDLFlBQU8sR0FBd0IsSUFBSSxDQUFDO1FBQ3BDLGFBQVEsR0FBNkIsSUFBSSxDQUFDO1FBQzFDLG9CQUFlLEdBQUcsSUFBSSxHQUFHLEVBSTdCLENBQUM7UUFDRyxtQkFBYyxHQUFHLENBQUMsQ0FBQztRQUNuQixjQUFTLEdBQUcsS0FBSyxDQUFDO1FBQ2xCLGVBQVUsR0FBa0IsSUFBSSxDQUFDO1FBQ3hCLHFCQUFnQixHQUFHLEtBQUssQ0FBQztJQW1RNUMsQ0FBQztJQWpRQzs7O09BR0c7SUFDSSxLQUFLLENBQUMsS0FBSztRQUNoQixJQUFJLENBQUMsVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNsRCxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ3JCLE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztRQUVELE9BQU8sSUFBSSxPQUFPLENBQVUsQ0FBQyxPQUFPLEVBQUUsRUFBRTtZQUN0QyxJQUFJLENBQUM7Z0JBQ0gsSUFBSSxDQUFDLE9BQU8sR0FBRyxLQUFLLENBQUMsSUFBSSxDQUFDLFVBQVcsRUFBRSxDQUFDLGNBQWMsQ0FBQyxFQUFFO29CQUN2RCxLQUFLLEVBQUUsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQztvQkFDL0IsR0FBRyxFQUFFLEVBQUUsR0FBRyxPQUFPLENBQUMsR0FBRyxFQUFFO2lCQUN4QixDQUFDLENBQUM7Z0JBRUgsOENBQThDO2dCQUM5QyxNQUFNLGFBQWEsR0FBRyxDQUFDLElBQVksRUFBRSxFQUFFO29CQUNyQyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO29CQUNoRSxLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUssRUFBRSxDQUFDO3dCQUN6QixNQUFNLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxlQUFlLElBQUksRUFBRSxFQUFFLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUM7b0JBQzNFLENBQUM7Z0JBQ0gsQ0FBQyxDQUFDO2dCQUNGLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxNQUFNLEVBQUUsYUFBYSxDQUFDLENBQUM7Z0JBRS9DLDJCQUEyQjtnQkFDM0IsSUFBSSxDQUFDLFFBQVEsR0FBRyxlQUFlLENBQUMsRUFBRSxLQUFLLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFPLEVBQUUsQ0FBQyxDQUFDO2dCQUNqRSxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFZLEVBQUUsRUFBRTtvQkFDeEMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztnQkFDL0IsQ0FBQyxDQUFDLENBQUM7Z0JBRUgsc0JBQXNCO2dCQUN0QixJQUFJLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLEVBQUU7b0JBQ3ZDLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLGtDQUFrQyxJQUFJLFlBQVksTUFBTSxHQUFHLEVBQUUsRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLENBQUMsQ0FBQztvQkFDOUcsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO29CQUNmLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxNQUFNLENBQUMsQ0FBQztnQkFDbEMsQ0FBQyxDQUFDLENBQUM7Z0JBRUgsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUU7b0JBQy9CLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLDRCQUE0QixHQUFHLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLENBQUMsQ0FBQztvQkFDN0YsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO29CQUNmLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFDakIsQ0FBQyxDQUFDLENBQUM7Z0JBRUgsdUNBQXVDO2dCQUN2QyxNQUFNLFlBQVksR0FBRyxVQUFVLENBQUMsR0FBRyxFQUFFO29CQUNuQyxNQUFNLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSwrQ0FBK0MsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDO29CQUNuRyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7b0JBQ1osT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUNqQixDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUM7Z0JBRVYsSUFBSSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxHQUFHLEVBQUU7b0JBQ2pDLFlBQVksQ0FBQyxZQUFZLENBQUMsQ0FBQztvQkFDM0IsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUM7b0JBQ3RCLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLDRCQUE0QixFQUFFLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUM7b0JBQy9FLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEIsQ0FBQyxDQUFDLENBQUM7WUFDTCxDQUFDO1lBQUMsT0FBTyxHQUFRLEVBQUUsQ0FBQztnQkFDbEIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsOEJBQThCLEdBQUcsQ0FBQyxPQUFPLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDO2dCQUMvRixPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDakIsQ0FBQztRQUNILENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLFdBQVcsQ0FBQyxNQUFjLEVBQUUsU0FBOEIsRUFBRTtRQUN2RSxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNyQyxNQUFNLElBQUksS0FBSyxDQUFDLGlDQUFpQyxDQUFDLENBQUM7UUFDckQsQ0FBQztRQUVELE1BQU0sRUFBRSxHQUFHLE9BQU8sRUFBRSxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDMUMsTUFBTSxPQUFPLEdBQXVCLEVBQUUsRUFBRSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsQ0FBQztRQUUzRCxPQUFPLElBQUksT0FBTyxDQUFNLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO1lBQzFDLE1BQU0sS0FBSyxHQUFHLFVBQVUsQ0FBQyxHQUFHLEVBQUU7Z0JBQzVCLElBQUksQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUNoQyxNQUFNLENBQUMsSUFBSSxLQUFLLENBQUMsc0JBQXNCLE1BQU0scUJBQXFCLElBQUksQ0FBQyxnQkFBZ0IsSUFBSSxDQUFDLENBQUMsQ0FBQztZQUNoRyxDQUFDLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUM7WUFFMUIsSUFBSSxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO1lBRXpELE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLEdBQUcsSUFBSSxDQUFDO1lBQzVDLElBQUksQ0FBQyxPQUFRLENBQUMsS0FBTSxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLEVBQUUsRUFBRTtnQkFDdkMsSUFBSSxHQUFHLEVBQUUsQ0FBQztvQkFDUixZQUFZLENBQUMsS0FBSyxDQUFDLENBQUM7b0JBQ3BCLElBQUksQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO29CQUNoQyxNQUFNLENBQUMsSUFBSSxLQUFLLENBQUMsdUNBQXVDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQzFFLENBQUM7WUFDSCxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELGtEQUFrRDtJQUUzQyxLQUFLLENBQUMsVUFBVSxDQUFDLE1BQVc7UUFDakMsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sRUFBRSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDOUMsQ0FBQztJQUVNLEtBQUssQ0FBQyxTQUFTO1FBQ3BCLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNqQyxDQUFDO0lBRU0sS0FBSyxDQUFDLFlBQVksQ0FBQyxNQUFzQjtRQUM5QyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsY0FBYyxFQUFFLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUNyRCxDQUFDO0lBRU0sS0FBSyxDQUFDLFVBQVU7UUFDckIsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQ3hDLENBQUM7SUFFTSxLQUFLLENBQUMsYUFBYTtRQUN4QixPQUFPLElBQUksQ0FBQyxXQUFXLENBQUMsZUFBZSxDQUFDLENBQUM7SUFDM0MsQ0FBQztJQUVNLEtBQUssQ0FBQyxvQkFBb0IsQ0FBQyxTQUFpQjtRQUNqRCxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsc0JBQXNCLEVBQUUsRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDO0lBQ2hFLENBQUM7SUFFTSxLQUFLLENBQUMsZ0JBQWdCLENBQUMsU0FBaUI7UUFDN0MsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLGtCQUFrQixFQUFFLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQztJQUM1RCxDQUFDO0lBRU0sS0FBSyxDQUFDLG9CQUFvQixDQUFDLFNBQWlCO1FBQ2pELE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQyxzQkFBc0IsRUFBRSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUM7SUFDakUsQ0FBQztJQUVNLEtBQUssQ0FBQyxpQkFBaUI7UUFDNUIsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLG1CQUFtQixDQUFDLENBQUM7UUFDM0QsT0FBTyxNQUFNLEVBQUUsS0FBSyxJQUFJLEVBQUUsQ0FBQztJQUM3QixDQUFDO0lBRU0sS0FBSyxDQUFDLGlCQUFpQjtRQUM1QixPQUFPLElBQUksQ0FBQyxXQUFXLENBQUMsbUJBQW1CLENBQUMsQ0FBQztJQUMvQyxDQUFDO0lBRU0sS0FBSyxDQUFDLHFCQUFxQixDQUFDLFVBQWtCO1FBQ25ELE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyx1QkFBdUIsRUFBRSxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUM7SUFDbEUsQ0FBQztJQUVNLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFZO1FBQ3hDLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxrQkFBa0IsRUFBRSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDdkQsQ0FBQztJQUVNLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxJQUFZO1FBQzNDLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxxQkFBcUIsRUFBRSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDMUQsQ0FBQztJQUVNLEtBQUssQ0FBQyxlQUFlLENBQUMsTUFBYyxFQUFFLElBQVksRUFBRSxHQUFXLEVBQUUsRUFBVztRQUNqRixNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsaUJBQWlCLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ3ZFLENBQUM7SUFFRDs7T0FFRztJQUNJLElBQUk7UUFDVCxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNqQixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDO1lBQzFCLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDO1lBQ3BCLElBQUksQ0FBQyxTQUFTLEdBQUcsS0FBSyxDQUFDO1lBRXZCLHFDQUFxQztZQUNyQyxJQUFJLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztnQkFDbEIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDdEIsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUM7WUFDdkIsQ0FBQztZQUVELDBCQUEwQjtZQUMxQixLQUFLLE1BQU0sQ0FBQyxFQUFFLE9BQU8sQ0FBQyxJQUFJLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztnQkFDL0MsWUFBWSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFDNUIsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQywwQkFBMEIsQ0FBQyxDQUFDLENBQUM7WUFDeEQsQ0FBQztZQUNELElBQUksQ0FBQyxlQUFlLENBQUMsS0FBSyxFQUFFLENBQUM7WUFFN0IsbURBQW1EO1lBQ25ELElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1lBQzFCLElBQUksQ0FBQyxNQUFNLEVBQUUsa0JBQWtCLEVBQUUsQ0FBQztZQUNsQyxJQUFJLENBQUMsTUFBTSxFQUFFLGtCQUFrQixFQUFFLENBQUM7WUFDbEMsSUFBSSxDQUFDLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxDQUFDO1lBRWpDLG1CQUFtQjtZQUNuQixJQUFJLENBQUM7Z0JBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUFDLENBQUM7WUFBQyxNQUFNLENBQUMsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO1lBRTFELDBDQUEwQztZQUMxQyxJQUFJLENBQUM7Z0JBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsQ0FBQztZQUFDLENBQUM7WUFBQyxNQUFNLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQztZQUNyRCxJQUFJLENBQUM7Z0JBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsQ0FBQztZQUFDLENBQUM7WUFBQyxNQUFNLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQztZQUN0RCxJQUFJLENBQUM7Z0JBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsQ0FBQztZQUFDLENBQUM7WUFBQyxNQUFNLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQztZQUV0RCw0Q0FBNEM7WUFDNUMsSUFBSSxDQUFDO2dCQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUFDLENBQUM7WUFBQyxNQUFNLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQztZQUU1Qyw2QkFBNkI7WUFDN0IsVUFBVSxDQUFDLEdBQUcsRUFBRTtnQkFDZCxJQUFJLENBQUM7b0JBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztnQkFBQyxDQUFDO2dCQUFDLE1BQU0sQ0FBQyxDQUFDLGtCQUFrQixDQUFDLENBQUM7WUFDNUQsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ25CLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFXLE9BQU87UUFDaEIsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDO0lBQ3hCLENBQUM7SUFFTyxVQUFVLENBQUMsSUFBWTtRQUM3QixJQUFJLENBQUMsSUFBSTtZQUFFLE9BQU87UUFFbEIsSUFBSSxNQUFXLENBQUM7UUFDaEIsSUFBSSxDQUFDO1lBQ0gsTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDNUIsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLG1DQUFtQyxJQUFJLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDO1lBQzVGLE9BQU87UUFDVCxDQUFDO1FBRUQsNkNBQTZDO1FBQzdDLElBQUksT0FBTyxJQUFJLE1BQU0sRUFBRSxDQUFDO1lBQ3RCLE1BQU0sS0FBSyxHQUFHLE1BQTBCLENBQUM7WUFDekMsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDbkQsT0FBTztRQUNULENBQUM7UUFFRCw2Q0FBNkM7UUFDN0MsSUFBSSxJQUFJLElBQUksTUFBTSxFQUFFLENBQUM7WUFDbkIsTUFBTSxRQUFRLEdBQUcsTUFBNkIsQ0FBQztZQUMvQyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDdEQsSUFBSSxPQUFPLEVBQUUsQ0FBQztnQkFDWixZQUFZLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUM1QixJQUFJLENBQUMsZUFBZSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUM7Z0JBQ3pDLElBQUksUUFBUSxDQUFDLE9BQU8sRUFBRSxDQUFDO29CQUNyQixPQUFPLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFDbkMsQ0FBQztxQkFBTSxDQUFDO29CQUNOLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxLQUFLLENBQUMsUUFBUSxDQUFDLEtBQUssSUFBSSw4QkFBOEIsQ0FBQyxDQUFDLENBQUM7Z0JBQzlFLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFTyxPQUFPO1FBQ2IsSUFBSSxDQUFDLFNBQVMsR0FBRyxLQUFLLENBQUM7UUFDdkIsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUM7UUFFcEIsSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDbEIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUN0QixJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQztRQUN2QixDQUFDO1FBRUQsOEJBQThCO1FBQzlCLEtBQUssTUFBTSxDQUFDLEVBQUUsRUFBRSxPQUFPLENBQUMsSUFBSSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDakQsWUFBWSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLDBCQUEwQixDQUFDLENBQUMsQ0FBQztRQUN4RCxDQUFDO1FBQ0QsSUFBSSxDQUFDLGVBQWUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUMvQixDQUFDO0NBQ0YifQ==