@push.rocks/smartproxy 13.1.2 → 15.0.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 (29) hide show
  1. package/dist_ts/00_commitinfo_data.js +3 -3
  2. package/dist_ts/proxies/smart-proxy/index.d.ts +5 -3
  3. package/dist_ts/proxies/smart-proxy/index.js +9 -5
  4. package/dist_ts/proxies/smart-proxy/models/index.d.ts +2 -0
  5. package/dist_ts/proxies/smart-proxy/models/index.js +2 -1
  6. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +82 -15
  7. package/dist_ts/proxies/smart-proxy/models/interfaces.js +10 -1
  8. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +133 -0
  9. package/dist_ts/proxies/smart-proxy/models/route-types.js +2 -0
  10. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +55 -0
  11. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +804 -0
  12. package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +127 -0
  13. package/dist_ts/proxies/smart-proxy/route-helpers.js +196 -0
  14. package/dist_ts/proxies/smart-proxy/route-manager.d.ts +103 -0
  15. package/dist_ts/proxies/smart-proxy/route-manager.js +483 -0
  16. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +19 -8
  17. package/dist_ts/proxies/smart-proxy/smart-proxy.js +239 -46
  18. package/package.json +2 -2
  19. package/readme.md +863 -423
  20. package/readme.plan.md +311 -250
  21. package/ts/00_commitinfo_data.ts +2 -2
  22. package/ts/proxies/smart-proxy/index.ts +20 -4
  23. package/ts/proxies/smart-proxy/models/index.ts +4 -0
  24. package/ts/proxies/smart-proxy/models/interfaces.ts +91 -13
  25. package/ts/proxies/smart-proxy/models/route-types.ts +184 -0
  26. package/ts/proxies/smart-proxy/route-connection-handler.ts +1117 -0
  27. package/ts/proxies/smart-proxy/route-helpers.ts +344 -0
  28. package/ts/proxies/smart-proxy/route-manager.ts +587 -0
  29. package/ts/proxies/smart-proxy/smart-proxy.ts +300 -69
@@ -0,0 +1,483 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { isRoutedOptions, isLegacyOptions } from './models/interfaces.js';
3
+ /**
4
+ * The RouteManager handles all routing decisions based on connections and attributes
5
+ */
6
+ export class RouteManager extends plugins.EventEmitter {
7
+ constructor(options) {
8
+ super();
9
+ this.routes = [];
10
+ this.portMap = new Map();
11
+ // We no longer support legacy options, always use provided options
12
+ this.options = options;
13
+ // Initialize routes from either source
14
+ this.updateRoutes(this.options.routes);
15
+ }
16
+ /**
17
+ * Update routes with new configuration
18
+ */
19
+ updateRoutes(routes = []) {
20
+ // Sort routes by priority (higher first)
21
+ this.routes = [...(routes || [])].sort((a, b) => {
22
+ const priorityA = a.priority ?? 0;
23
+ const priorityB = b.priority ?? 0;
24
+ return priorityB - priorityA;
25
+ });
26
+ // Rebuild port mapping for fast lookups
27
+ this.rebuildPortMap();
28
+ }
29
+ /**
30
+ * Rebuild the port mapping for fast lookups
31
+ */
32
+ rebuildPortMap() {
33
+ this.portMap.clear();
34
+ for (const route of this.routes) {
35
+ const ports = this.expandPortRange(route.match.ports);
36
+ for (const port of ports) {
37
+ if (!this.portMap.has(port)) {
38
+ this.portMap.set(port, []);
39
+ }
40
+ this.portMap.get(port).push(route);
41
+ }
42
+ }
43
+ }
44
+ /**
45
+ * Expand a port range specification into an array of individual ports
46
+ */
47
+ expandPortRange(portRange) {
48
+ if (typeof portRange === 'number') {
49
+ return [portRange];
50
+ }
51
+ if (Array.isArray(portRange)) {
52
+ // Handle array of port objects or numbers
53
+ return portRange.flatMap(item => {
54
+ if (typeof item === 'number') {
55
+ return [item];
56
+ }
57
+ else if (typeof item === 'object' && 'from' in item && 'to' in item) {
58
+ // Handle port range object
59
+ const ports = [];
60
+ for (let p = item.from; p <= item.to; p++) {
61
+ ports.push(p);
62
+ }
63
+ return ports;
64
+ }
65
+ return [];
66
+ });
67
+ }
68
+ return [];
69
+ }
70
+ /**
71
+ * Get all ports that should be listened on
72
+ */
73
+ getListeningPorts() {
74
+ return Array.from(this.portMap.keys());
75
+ }
76
+ /**
77
+ * Get all routes for a given port
78
+ */
79
+ getRoutesForPort(port) {
80
+ return this.portMap.get(port) || [];
81
+ }
82
+ /**
83
+ * Test if a pattern matches a domain using glob matching
84
+ */
85
+ matchDomain(pattern, domain) {
86
+ // Convert glob pattern to regex
87
+ const regexPattern = pattern
88
+ .replace(/\./g, '\\.') // Escape dots
89
+ .replace(/\*/g, '.*'); // Convert * to .*
90
+ const regex = new RegExp(`^${regexPattern}$`, 'i');
91
+ return regex.test(domain);
92
+ }
93
+ /**
94
+ * Match a domain against all patterns in a route
95
+ */
96
+ matchRouteDomain(route, domain) {
97
+ if (!route.match.domains) {
98
+ // If no domains specified, match all domains
99
+ return true;
100
+ }
101
+ const patterns = Array.isArray(route.match.domains)
102
+ ? route.match.domains
103
+ : [route.match.domains];
104
+ return patterns.some(pattern => this.matchDomain(pattern, domain));
105
+ }
106
+ /**
107
+ * Check if a client IP is allowed by a route's security settings
108
+ */
109
+ isClientIpAllowed(route, clientIp) {
110
+ const security = route.action.security;
111
+ if (!security) {
112
+ return true; // No security settings means allowed
113
+ }
114
+ // Check blocked IPs first
115
+ if (security.blockedIps && security.blockedIps.length > 0) {
116
+ for (const pattern of security.blockedIps) {
117
+ if (this.matchIpPattern(pattern, clientIp)) {
118
+ return false; // IP is blocked
119
+ }
120
+ }
121
+ }
122
+ // If there are allowed IPs, check them
123
+ if (security.allowedIps && security.allowedIps.length > 0) {
124
+ for (const pattern of security.allowedIps) {
125
+ if (this.matchIpPattern(pattern, clientIp)) {
126
+ return true; // IP is allowed
127
+ }
128
+ }
129
+ return false; // IP not in allowed list
130
+ }
131
+ // No allowed IPs specified, so IP is allowed
132
+ return true;
133
+ }
134
+ /**
135
+ * Match an IP against a pattern
136
+ */
137
+ matchIpPattern(pattern, ip) {
138
+ // Handle exact match
139
+ if (pattern === ip) {
140
+ return true;
141
+ }
142
+ // Handle CIDR notation (e.g., 192.168.1.0/24)
143
+ if (pattern.includes('/')) {
144
+ return this.matchIpCidr(pattern, ip);
145
+ }
146
+ // Handle glob pattern (e.g., 192.168.1.*)
147
+ if (pattern.includes('*')) {
148
+ const regexPattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*');
149
+ const regex = new RegExp(`^${regexPattern}$`);
150
+ return regex.test(ip);
151
+ }
152
+ return false;
153
+ }
154
+ /**
155
+ * Match an IP against a CIDR pattern
156
+ */
157
+ matchIpCidr(cidr, ip) {
158
+ try {
159
+ // In a real implementation, you'd use a proper IP library
160
+ // This is a simplified implementation
161
+ const [subnet, bits] = cidr.split('/');
162
+ const mask = parseInt(bits, 10);
163
+ // Convert IP addresses to numeric values
164
+ const ipNum = this.ipToNumber(ip);
165
+ const subnetNum = this.ipToNumber(subnet);
166
+ // Calculate subnet mask
167
+ const maskNum = ~(2 ** (32 - mask) - 1);
168
+ // Check if IP is in subnet
169
+ return (ipNum & maskNum) === (subnetNum & maskNum);
170
+ }
171
+ catch (e) {
172
+ console.error(`Error matching IP ${ip} against CIDR ${cidr}:`, e);
173
+ return false;
174
+ }
175
+ }
176
+ /**
177
+ * Convert an IP address to a numeric value
178
+ */
179
+ ipToNumber(ip) {
180
+ const parts = ip.split('.').map(part => parseInt(part, 10));
181
+ return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3];
182
+ }
183
+ /**
184
+ * Find the matching route for a connection
185
+ */
186
+ findMatchingRoute(options) {
187
+ const { port, domain, clientIp, path, tlsVersion } = options;
188
+ // Get all routes for this port
189
+ const routesForPort = this.getRoutesForPort(port);
190
+ // Find the first matching route based on priority order
191
+ for (const route of routesForPort) {
192
+ // Check domain match if specified
193
+ if (domain && !this.matchRouteDomain(route, domain)) {
194
+ continue;
195
+ }
196
+ // Check path match if specified in both route and request
197
+ if (path && route.match.path) {
198
+ if (!this.matchPath(route.match.path, path)) {
199
+ continue;
200
+ }
201
+ }
202
+ // Check client IP match
203
+ if (route.match.clientIp && !route.match.clientIp.some(pattern => this.matchIpPattern(pattern, clientIp))) {
204
+ continue;
205
+ }
206
+ // Check TLS version match
207
+ if (tlsVersion && route.match.tlsVersion &&
208
+ !route.match.tlsVersion.includes(tlsVersion)) {
209
+ continue;
210
+ }
211
+ // Check security settings
212
+ if (!this.isClientIpAllowed(route, clientIp)) {
213
+ continue;
214
+ }
215
+ // All checks passed, this route matches
216
+ return { route };
217
+ }
218
+ return null;
219
+ }
220
+ /**
221
+ * Match a path against a pattern
222
+ */
223
+ matchPath(pattern, path) {
224
+ // Convert the glob pattern to a regex
225
+ const regexPattern = pattern
226
+ .replace(/\./g, '\\.') // Escape dots
227
+ .replace(/\*/g, '.*') // Convert * to .*
228
+ .replace(/\//g, '\\/'); // Escape slashes
229
+ const regex = new RegExp(`^${regexPattern}$`);
230
+ return regex.test(path);
231
+ }
232
+ /**
233
+ * Convert a domain config to routes
234
+ * (For backward compatibility with code that still uses domainConfigs)
235
+ */
236
+ domainConfigToRoutes(domainConfig) {
237
+ const routes = [];
238
+ const { domains, forwarding } = domainConfig;
239
+ // Determine the action based on forwarding type
240
+ let action = {
241
+ type: 'forward',
242
+ target: {
243
+ host: forwarding.target.host,
244
+ port: forwarding.target.port
245
+ }
246
+ };
247
+ // Set TLS mode based on forwarding type
248
+ switch (forwarding.type) {
249
+ case 'http-only':
250
+ // No TLS settings needed
251
+ break;
252
+ case 'https-passthrough':
253
+ action.tls = { mode: 'passthrough' };
254
+ break;
255
+ case 'https-terminate-to-http':
256
+ action.tls = {
257
+ mode: 'terminate',
258
+ certificate: forwarding.https?.customCert ? {
259
+ key: forwarding.https.customCert.key,
260
+ cert: forwarding.https.customCert.cert
261
+ } : 'auto'
262
+ };
263
+ break;
264
+ case 'https-terminate-to-https':
265
+ action.tls = {
266
+ mode: 'terminate-and-reencrypt',
267
+ certificate: forwarding.https?.customCert ? {
268
+ key: forwarding.https.customCert.key,
269
+ cert: forwarding.https.customCert.cert
270
+ } : 'auto'
271
+ };
272
+ break;
273
+ }
274
+ // Add security settings if present
275
+ if (forwarding.security) {
276
+ action.security = {
277
+ allowedIps: forwarding.security.allowedIps,
278
+ blockedIps: forwarding.security.blockedIps,
279
+ maxConnections: forwarding.security.maxConnections
280
+ };
281
+ }
282
+ // Add advanced settings if present
283
+ if (forwarding.advanced) {
284
+ action.advanced = {
285
+ timeout: forwarding.advanced.timeout,
286
+ headers: forwarding.advanced.headers,
287
+ keepAlive: forwarding.advanced.keepAlive
288
+ };
289
+ }
290
+ // Determine which port to use based on forwarding type
291
+ const defaultPort = forwarding.type.startsWith('https') ? 443 : 80;
292
+ // Add the main route
293
+ routes.push({
294
+ match: {
295
+ ports: defaultPort,
296
+ domains
297
+ },
298
+ action,
299
+ name: `Route for ${domains.join(', ')}`
300
+ });
301
+ // Add HTTP redirect if needed
302
+ if (forwarding.http?.redirectToHttps) {
303
+ routes.push({
304
+ match: {
305
+ ports: 80,
306
+ domains
307
+ },
308
+ action: {
309
+ type: 'redirect',
310
+ redirect: {
311
+ to: 'https://{domain}{path}',
312
+ status: 301
313
+ }
314
+ },
315
+ name: `HTTP Redirect for ${domains.join(', ')}`,
316
+ priority: 100 // Higher priority for redirects
317
+ });
318
+ }
319
+ // Add port ranges if specified
320
+ if (forwarding.advanced?.portRanges) {
321
+ for (const range of forwarding.advanced.portRanges) {
322
+ routes.push({
323
+ match: {
324
+ ports: [{ from: range.from, to: range.to }],
325
+ domains
326
+ },
327
+ action,
328
+ name: `Port Range ${range.from}-${range.to} for ${domains.join(', ')}`
329
+ });
330
+ }
331
+ }
332
+ return routes;
333
+ }
334
+ /**
335
+ * Update routes based on domain configs
336
+ * (For backward compatibility with code that still uses domainConfigs)
337
+ */
338
+ updateFromDomainConfigs(domainConfigs) {
339
+ const routes = [];
340
+ // Convert each domain config to routes
341
+ for (const config of domainConfigs) {
342
+ routes.push(...this.domainConfigToRoutes(config));
343
+ }
344
+ // Merge with existing routes that aren't derived from domain configs
345
+ const nonDomainRoutes = this.routes.filter(r => !r.name || !r.name.includes('for '));
346
+ this.updateRoutes([...nonDomainRoutes, ...routes]);
347
+ }
348
+ /**
349
+ * Validate the route configuration and return any warnings
350
+ */
351
+ validateConfiguration() {
352
+ const warnings = [];
353
+ const duplicatePorts = new Map();
354
+ // Check for routes with the same exact match criteria
355
+ for (let i = 0; i < this.routes.length; i++) {
356
+ for (let j = i + 1; j < this.routes.length; j++) {
357
+ const route1 = this.routes[i];
358
+ const route2 = this.routes[j];
359
+ // Check if route match criteria are the same
360
+ if (this.areMatchesSimilar(route1.match, route2.match)) {
361
+ warnings.push(`Routes "${route1.name || i}" and "${route2.name || j}" have similar match criteria. ` +
362
+ `The route with higher priority (${Math.max(route1.priority || 0, route2.priority || 0)}) will be used.`);
363
+ }
364
+ }
365
+ }
366
+ // Check for routes that may never be matched due to priority
367
+ for (let i = 0; i < this.routes.length; i++) {
368
+ const route = this.routes[i];
369
+ const higherPriorityRoutes = this.routes.filter(r => (r.priority || 0) > (route.priority || 0));
370
+ for (const higherRoute of higherPriorityRoutes) {
371
+ if (this.isRouteShadowed(route, higherRoute)) {
372
+ warnings.push(`Route "${route.name || i}" may never be matched because it is shadowed by ` +
373
+ `higher priority route "${higherRoute.name || 'unnamed'}"`);
374
+ break;
375
+ }
376
+ }
377
+ }
378
+ return warnings;
379
+ }
380
+ /**
381
+ * Check if two route matches are similar (potential conflict)
382
+ */
383
+ areMatchesSimilar(match1, match2) {
384
+ // Check port overlap
385
+ const ports1 = new Set(this.expandPortRange(match1.ports));
386
+ const ports2 = new Set(this.expandPortRange(match2.ports));
387
+ let havePortOverlap = false;
388
+ for (const port of ports1) {
389
+ if (ports2.has(port)) {
390
+ havePortOverlap = true;
391
+ break;
392
+ }
393
+ }
394
+ if (!havePortOverlap) {
395
+ return false;
396
+ }
397
+ // Check domain overlap
398
+ if (match1.domains && match2.domains) {
399
+ const domains1 = Array.isArray(match1.domains) ? match1.domains : [match1.domains];
400
+ const domains2 = Array.isArray(match2.domains) ? match2.domains : [match2.domains];
401
+ // Check if any domain pattern from match1 could match any from match2
402
+ let haveDomainOverlap = false;
403
+ for (const domain1 of domains1) {
404
+ for (const domain2 of domains2) {
405
+ if (domain1 === domain2 ||
406
+ (domain1.includes('*') || domain2.includes('*'))) {
407
+ haveDomainOverlap = true;
408
+ break;
409
+ }
410
+ }
411
+ if (haveDomainOverlap)
412
+ break;
413
+ }
414
+ if (!haveDomainOverlap) {
415
+ return false;
416
+ }
417
+ }
418
+ else if (match1.domains || match2.domains) {
419
+ // One has domains, the other doesn't - they could overlap
420
+ // The one with domains is more specific, so it's not exactly a conflict
421
+ return false;
422
+ }
423
+ // Check path overlap
424
+ if (match1.path && match2.path) {
425
+ // This is a simplified check - in a real implementation,
426
+ // you'd need to check if the path patterns could match the same paths
427
+ return match1.path === match2.path ||
428
+ match1.path.includes('*') ||
429
+ match2.path.includes('*');
430
+ }
431
+ else if (match1.path || match2.path) {
432
+ // One has a path, the other doesn't
433
+ return false;
434
+ }
435
+ // If we get here, the matches have significant overlap
436
+ return true;
437
+ }
438
+ /**
439
+ * Check if a route is completely shadowed by a higher priority route
440
+ */
441
+ isRouteShadowed(route, higherPriorityRoute) {
442
+ // If they don't have similar match criteria, no shadowing occurs
443
+ if (!this.areMatchesSimilar(route.match, higherPriorityRoute.match)) {
444
+ return false;
445
+ }
446
+ // If higher priority route has more specific criteria, no shadowing
447
+ if (this.isRouteMoreSpecific(higherPriorityRoute.match, route.match)) {
448
+ return false;
449
+ }
450
+ // If higher priority route is equally or less specific but has higher priority,
451
+ // it shadows the lower priority route
452
+ return true;
453
+ }
454
+ /**
455
+ * Check if route1 is more specific than route2
456
+ */
457
+ isRouteMoreSpecific(match1, match2) {
458
+ // Check if match1 has more specific criteria
459
+ let match1Points = 0;
460
+ let match2Points = 0;
461
+ // Path is the most specific
462
+ if (match1.path)
463
+ match1Points += 3;
464
+ if (match2.path)
465
+ match2Points += 3;
466
+ // Domain is next most specific
467
+ if (match1.domains)
468
+ match1Points += 2;
469
+ if (match2.domains)
470
+ match2Points += 2;
471
+ // Client IP and TLS version are least specific
472
+ if (match1.clientIp)
473
+ match1Points += 1;
474
+ if (match2.clientIp)
475
+ match2Points += 1;
476
+ if (match1.tlsVersion)
477
+ match1Points += 1;
478
+ if (match2.tlsVersion)
479
+ match2Points += 1;
480
+ return match1Points > match2Points;
481
+ }
482
+ }
483
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtbWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvcm91dGUtbWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBWTVDLE9BQU8sRUFDTCxlQUFlLEVBQ2YsZUFBZSxFQUNoQixNQUFNLHdCQUF3QixDQUFDO0FBV2hDOztHQUVHO0FBQ0gsTUFBTSxPQUFPLFlBQWEsU0FBUSxPQUFPLENBQUMsWUFBWTtJQUtwRCxZQUFZLE9BQTJCO1FBQ3JDLEtBQUssRUFBRSxDQUFDO1FBTEYsV0FBTSxHQUFtQixFQUFFLENBQUM7UUFDNUIsWUFBTyxHQUFnQyxJQUFJLEdBQUcsRUFBRSxDQUFDO1FBTXZELG1FQUFtRTtRQUNuRSxJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztRQUV2Qix1Q0FBdUM7UUFDdkMsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3pDLENBQUM7SUFFRDs7T0FFRztJQUNJLFlBQVksQ0FBQyxTQUF5QixFQUFFO1FBQzdDLHlDQUF5QztRQUN6QyxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsR0FBRyxDQUFDLE1BQU0sSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUM5QyxNQUFNLFNBQVMsR0FBRyxDQUFDLENBQUMsUUFBUSxJQUFJLENBQUMsQ0FBQztZQUNsQyxNQUFNLFNBQVMsR0FBRyxDQUFDLENBQUMsUUFBUSxJQUFJLENBQUMsQ0FBQztZQUNsQyxPQUFPLFNBQVMsR0FBRyxTQUFTLENBQUM7UUFDL0IsQ0FBQyxDQUFDLENBQUM7UUFFSCx3Q0FBd0M7UUFDeEMsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO0lBQ3hCLENBQUM7SUFFRDs7T0FFRztJ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
@@ -1,9 +1,8 @@
1
1
  import * as plugins from '../../plugins.js';
2
- import { DomainConfigManager } from './domain-config-manager.js';
3
2
  import type { ISmartProxyOptions, IDomainConfig } from './models/interfaces.js';
4
- export type { ISmartProxyOptions as IPortProxySettings, IDomainConfig };
3
+ import type { IRouteConfig } from './models/route-types.js';
5
4
  /**
6
- * SmartProxy - Main class that coordinates all components
5
+ * SmartProxy - Unified route-based API
7
6
  */
8
7
  export declare class SmartProxy extends plugins.EventEmitter {
9
8
  private netServers;
@@ -11,17 +10,21 @@ export declare class SmartProxy extends plugins.EventEmitter {
11
10
  private isShuttingDown;
12
11
  private connectionManager;
13
12
  private securityManager;
14
- domainConfigManager: DomainConfigManager;
13
+ private domainConfigManager;
15
14
  private tlsManager;
16
15
  private networkProxyBridge;
17
16
  private timeoutManager;
18
17
  private portRangeManager;
19
- private connectionHandler;
18
+ private routeManager;
19
+ private routeConnectionHandler;
20
20
  private port80Handler;
21
21
  private certProvisioner?;
22
+ /**
23
+ * Constructor that supports both legacy and route-based configuration
24
+ */
22
25
  constructor(settingsArg: ISmartProxyOptions);
23
26
  /**
24
- * The settings for the port proxy
27
+ * The settings for the SmartProxy
25
28
  */
26
29
  settings: ISmartProxyOptions;
27
30
  /**
@@ -29,17 +32,25 @@ export declare class SmartProxy extends plugins.EventEmitter {
29
32
  */
30
33
  private initializePort80Handler;
31
34
  /**
32
- * Start the proxy server
35
+ * Start the proxy server with support for both configuration types
33
36
  */
34
37
  start(): Promise<void>;
38
+ /**
39
+ * Extract domain configurations from routes for certificate provisioning
40
+ */
41
+ private extractDomainConfigsFromRoutes;
35
42
  /**
36
43
  * Stop the proxy server
37
44
  */
38
45
  stop(): Promise<void>;
39
46
  /**
40
- * Updates the domain configurations for the proxy
47
+ * Updates the domain configurations for the proxy (legacy support)
41
48
  */
42
49
  updateDomainConfigs(newDomainConfigs: IDomainConfig[]): Promise<void>;
50
+ /**
51
+ * Update routes with new configuration (new API)
52
+ */
53
+ updateRoutes(newRoutes: IRouteConfig[]): Promise<void>;
43
54
  /**
44
55
  * Request a certificate for a specific domain
45
56
  */