@push.rocks/smartproxy 16.0.2 → 16.0.3

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 (115) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/core/models/index.d.ts +2 -0
  3. package/dist_ts/core/models/index.js +3 -1
  4. package/dist_ts/core/models/route-context.d.ts +62 -0
  5. package/dist_ts/core/models/route-context.js +43 -0
  6. package/dist_ts/core/models/socket-augmentation.d.ts +12 -0
  7. package/dist_ts/core/models/socket-augmentation.js +18 -0
  8. package/dist_ts/core/utils/event-system.d.ts +200 -0
  9. package/dist_ts/core/utils/event-system.js +224 -0
  10. package/dist_ts/core/utils/index.d.ts +7 -0
  11. package/dist_ts/core/utils/index.js +8 -1
  12. package/dist_ts/core/utils/route-manager.d.ts +118 -0
  13. package/dist_ts/core/utils/route-manager.js +383 -0
  14. package/dist_ts/core/utils/route-utils.d.ts +94 -0
  15. package/dist_ts/core/utils/route-utils.js +264 -0
  16. package/dist_ts/core/utils/security-utils.d.ts +111 -0
  17. package/dist_ts/core/utils/security-utils.js +212 -0
  18. package/dist_ts/core/utils/shared-security-manager.d.ts +110 -0
  19. package/dist_ts/core/utils/shared-security-manager.js +252 -0
  20. package/dist_ts/core/utils/template-utils.d.ts +37 -0
  21. package/dist_ts/core/utils/template-utils.js +104 -0
  22. package/dist_ts/core/utils/websocket-utils.d.ts +23 -0
  23. package/dist_ts/core/utils/websocket-utils.js +86 -0
  24. package/dist_ts/http/router/index.d.ts +5 -1
  25. package/dist_ts/http/router/index.js +4 -2
  26. package/dist_ts/http/router/route-router.d.ts +108 -0
  27. package/dist_ts/http/router/route-router.js +393 -0
  28. package/dist_ts/index.d.ts +8 -2
  29. package/dist_ts/index.js +10 -3
  30. package/dist_ts/proxies/index.d.ts +7 -2
  31. package/dist_ts/proxies/index.js +10 -4
  32. package/dist_ts/proxies/network-proxy/certificate-manager.d.ts +21 -0
  33. package/dist_ts/proxies/network-proxy/certificate-manager.js +92 -1
  34. package/dist_ts/proxies/network-proxy/context-creator.d.ts +34 -0
  35. package/dist_ts/proxies/network-proxy/context-creator.js +108 -0
  36. package/dist_ts/proxies/network-proxy/function-cache.d.ts +90 -0
  37. package/dist_ts/proxies/network-proxy/function-cache.js +198 -0
  38. package/dist_ts/proxies/network-proxy/http-request-handler.d.ts +40 -0
  39. package/dist_ts/proxies/network-proxy/http-request-handler.js +256 -0
  40. package/dist_ts/proxies/network-proxy/http2-request-handler.d.ts +24 -0
  41. package/dist_ts/proxies/network-proxy/http2-request-handler.js +201 -0
  42. package/dist_ts/proxies/network-proxy/models/types.d.ts +73 -1
  43. package/dist_ts/proxies/network-proxy/models/types.js +242 -1
  44. package/dist_ts/proxies/network-proxy/network-proxy.d.ts +23 -20
  45. package/dist_ts/proxies/network-proxy/network-proxy.js +147 -60
  46. package/dist_ts/proxies/network-proxy/request-handler.d.ts +38 -5
  47. package/dist_ts/proxies/network-proxy/request-handler.js +584 -198
  48. package/dist_ts/proxies/network-proxy/security-manager.d.ts +65 -0
  49. package/dist_ts/proxies/network-proxy/security-manager.js +255 -0
  50. package/dist_ts/proxies/network-proxy/websocket-handler.d.ts +13 -2
  51. package/dist_ts/proxies/network-proxy/websocket-handler.js +238 -20
  52. package/dist_ts/proxies/smart-proxy/index.d.ts +1 -1
  53. package/dist_ts/proxies/smart-proxy/index.js +3 -3
  54. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +3 -5
  55. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +56 -3
  56. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +4 -57
  57. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +19 -228
  58. package/dist_ts/proxies/smart-proxy/port-manager.d.ts +81 -0
  59. package/dist_ts/proxies/smart-proxy/port-manager.js +166 -0
  60. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +5 -0
  61. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +131 -15
  62. package/dist_ts/proxies/smart-proxy/route-helpers/index.d.ts +3 -1
  63. package/dist_ts/proxies/smart-proxy/route-helpers/index.js +5 -3
  64. package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +5 -178
  65. package/dist_ts/proxies/smart-proxy/route-helpers.js +8 -296
  66. package/dist_ts/proxies/smart-proxy/route-manager.d.ts +11 -2
  67. package/dist_ts/proxies/smart-proxy/route-manager.js +79 -10
  68. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +29 -2
  69. package/dist_ts/proxies/smart-proxy/smart-proxy.js +48 -43
  70. package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +67 -1
  71. package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +120 -1
  72. package/dist_ts/proxies/smart-proxy/utils/route-validators.d.ts +3 -3
  73. package/dist_ts/proxies/smart-proxy/utils/route-validators.js +27 -5
  74. package/package.json +1 -1
  75. package/readme.md +102 -14
  76. package/readme.plan.md +103 -168
  77. package/ts/00_commitinfo_data.ts +1 -1
  78. package/ts/core/models/index.ts +2 -0
  79. package/ts/core/models/route-context.ts +113 -0
  80. package/ts/core/models/socket-augmentation.ts +33 -0
  81. package/ts/core/utils/event-system.ts +376 -0
  82. package/ts/core/utils/index.ts +7 -0
  83. package/ts/core/utils/route-manager.ts +489 -0
  84. package/ts/core/utils/route-utils.ts +312 -0
  85. package/ts/core/utils/security-utils.ts +309 -0
  86. package/ts/core/utils/shared-security-manager.ts +333 -0
  87. package/ts/core/utils/template-utils.ts +124 -0
  88. package/ts/core/utils/websocket-utils.ts +81 -0
  89. package/ts/http/router/index.ts +8 -1
  90. package/ts/http/router/route-router.ts +482 -0
  91. package/ts/index.ts +14 -2
  92. package/ts/proxies/index.ts +12 -3
  93. package/ts/proxies/network-proxy/certificate-manager.ts +114 -10
  94. package/ts/proxies/network-proxy/context-creator.ts +145 -0
  95. package/ts/proxies/network-proxy/function-cache.ts +259 -0
  96. package/ts/proxies/network-proxy/http-request-handler.ts +330 -0
  97. package/ts/proxies/network-proxy/http2-request-handler.ts +255 -0
  98. package/ts/proxies/network-proxy/models/types.ts +312 -1
  99. package/ts/proxies/network-proxy/network-proxy.ts +195 -86
  100. package/ts/proxies/network-proxy/request-handler.ts +698 -246
  101. package/ts/proxies/network-proxy/security-manager.ts +298 -0
  102. package/ts/proxies/network-proxy/websocket-handler.ts +276 -33
  103. package/ts/proxies/smart-proxy/index.ts +2 -12
  104. package/ts/proxies/smart-proxy/models/interfaces.ts +7 -4
  105. package/ts/proxies/smart-proxy/models/route-types.ts +78 -10
  106. package/ts/proxies/smart-proxy/network-proxy-bridge.ts +20 -257
  107. package/ts/proxies/smart-proxy/port-manager.ts +195 -0
  108. package/ts/proxies/smart-proxy/route-connection-handler.ts +156 -21
  109. package/ts/proxies/smart-proxy/route-manager.ts +98 -14
  110. package/ts/proxies/smart-proxy/smart-proxy.ts +56 -55
  111. package/ts/proxies/smart-proxy/utils/route-helpers.ts +167 -1
  112. package/ts/proxies/smart-proxy/utils/route-validators.ts +24 -5
  113. package/ts/proxies/smart-proxy/domain-config-manager.ts.bak +0 -441
  114. package/ts/proxies/smart-proxy/route-helpers/index.ts +0 -9
  115. package/ts/proxies/smart-proxy/route-helpers.ts +0 -498
@@ -0,0 +1,489 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import type {
3
+ IRouteConfig,
4
+ IRouteMatch,
5
+ IRouteAction,
6
+ TPortRange,
7
+ IRouteContext
8
+ } from '../../proxies/smart-proxy/models/route-types.js';
9
+ import {
10
+ matchDomain,
11
+ matchRouteDomain,
12
+ matchPath,
13
+ matchIpPattern,
14
+ matchIpCidr,
15
+ ipToNumber,
16
+ isIpAuthorized,
17
+ calculateRouteSpecificity
18
+ } from './route-utils.js';
19
+
20
+ /**
21
+ * Result of route matching
22
+ */
23
+ export interface IRouteMatchResult {
24
+ route: IRouteConfig;
25
+ // Additional match parameters (path, query, etc.)
26
+ params?: Record<string, string>;
27
+ }
28
+
29
+ /**
30
+ * Logger interface for RouteManager
31
+ */
32
+ export interface ILogger {
33
+ info: (message: string, ...args: any[]) => void;
34
+ warn: (message: string, ...args: any[]) => void;
35
+ error: (message: string, ...args: any[]) => void;
36
+ debug?: (message: string, ...args: any[]) => void;
37
+ }
38
+
39
+ /**
40
+ * Shared RouteManager used by both SmartProxy and NetworkProxy
41
+ *
42
+ * This provides a unified implementation for route management,
43
+ * route matching, and port handling.
44
+ */
45
+ export class SharedRouteManager extends plugins.EventEmitter {
46
+ private routes: IRouteConfig[] = [];
47
+ private portMap: Map<number, IRouteConfig[]> = new Map();
48
+ private logger: ILogger;
49
+ private enableDetailedLogging: boolean;
50
+
51
+ /**
52
+ * Memoization cache for expanded port ranges
53
+ */
54
+ private portRangeCache: Map<string, number[]> = new Map();
55
+
56
+ constructor(options: {
57
+ logger?: ILogger;
58
+ enableDetailedLogging?: boolean;
59
+ routes?: IRouteConfig[];
60
+ }) {
61
+ super();
62
+
63
+ // Set up logger (use console if not provided)
64
+ this.logger = options.logger || {
65
+ info: console.log,
66
+ warn: console.warn,
67
+ error: console.error,
68
+ debug: options.enableDetailedLogging ? console.log : undefined
69
+ };
70
+
71
+ this.enableDetailedLogging = options.enableDetailedLogging || false;
72
+
73
+ // Initialize routes if provided
74
+ if (options.routes) {
75
+ this.updateRoutes(options.routes);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Update routes with new configuration
81
+ */
82
+ public updateRoutes(routes: IRouteConfig[] = []): void {
83
+ // Sort routes by priority (higher first)
84
+ this.routes = [...(routes || [])].sort((a, b) => {
85
+ const priorityA = a.priority ?? 0;
86
+ const priorityB = b.priority ?? 0;
87
+ return priorityB - priorityA;
88
+ });
89
+
90
+ // Rebuild port mapping for fast lookups
91
+ this.rebuildPortMap();
92
+
93
+ this.logger.info(`Updated RouteManager with ${this.routes.length} routes`);
94
+ }
95
+
96
+ /**
97
+ * Get all routes
98
+ */
99
+ public getRoutes(): IRouteConfig[] {
100
+ return [...this.routes];
101
+ }
102
+
103
+ /**
104
+ * Rebuild the port mapping for fast lookups
105
+ * Also logs information about the ports being listened on
106
+ */
107
+ private rebuildPortMap(): void {
108
+ this.portMap.clear();
109
+ this.portRangeCache.clear(); // Clear cache when rebuilding
110
+
111
+ // Track ports for logging
112
+ const portToRoutesMap = new Map<number, string[]>();
113
+
114
+ for (const route of this.routes) {
115
+ const ports = this.expandPortRange(route.match.ports);
116
+
117
+ // Skip if no ports were found
118
+ if (ports.length === 0) {
119
+ this.logger.warn(`Route ${route.name || 'unnamed'} has no valid ports to listen on`);
120
+ continue;
121
+ }
122
+
123
+ for (const port of ports) {
124
+ // Add to portMap for routing
125
+ if (!this.portMap.has(port)) {
126
+ this.portMap.set(port, []);
127
+ }
128
+ this.portMap.get(port)!.push(route);
129
+
130
+ // Add to tracking for logging
131
+ if (!portToRoutesMap.has(port)) {
132
+ portToRoutesMap.set(port, []);
133
+ }
134
+ portToRoutesMap.get(port)!.push(route.name || 'unnamed');
135
+ }
136
+ }
137
+
138
+ // Log summary of ports and routes
139
+ const totalPorts = this.portMap.size;
140
+ const totalRoutes = this.routes.length;
141
+ this.logger.info(`Route manager configured with ${totalRoutes} routes across ${totalPorts} ports`);
142
+
143
+ // Log port details if detailed logging is enabled
144
+ if (this.enableDetailedLogging) {
145
+ for (const [port, routes] of this.portMap.entries()) {
146
+ this.logger.info(`Port ${port}: ${routes.length} routes (${portToRoutesMap.get(port)!.join(', ')})`);
147
+ }
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Expand a port range specification into an array of individual ports
153
+ * Uses caching to improve performance for frequently used port ranges
154
+ *
155
+ * @public - Made public to allow external code to interpret port ranges
156
+ */
157
+ public expandPortRange(portRange: TPortRange): number[] {
158
+ // For simple number, return immediately
159
+ if (typeof portRange === 'number') {
160
+ return [portRange];
161
+ }
162
+
163
+ // Create a cache key for this port range
164
+ const cacheKey = JSON.stringify(portRange);
165
+
166
+ // Check if we have a cached result
167
+ if (this.portRangeCache.has(cacheKey)) {
168
+ return this.portRangeCache.get(cacheKey)!;
169
+ }
170
+
171
+ // Process the port range
172
+ let result: number[] = [];
173
+
174
+ if (Array.isArray(portRange)) {
175
+ // Handle array of port objects or numbers
176
+ result = portRange.flatMap(item => {
177
+ if (typeof item === 'number') {
178
+ return [item];
179
+ } else if (typeof item === 'object' && 'from' in item && 'to' in item) {
180
+ // Handle port range object - check valid range
181
+ if (item.from > item.to) {
182
+ this.logger.warn(`Invalid port range: from (${item.from}) > to (${item.to})`);
183
+ return [];
184
+ }
185
+
186
+ // Handle port range object
187
+ const ports: number[] = [];
188
+ for (let p = item.from; p <= item.to; p++) {
189
+ ports.push(p);
190
+ }
191
+ return ports;
192
+ }
193
+ return [];
194
+ });
195
+ }
196
+
197
+ // Cache the result
198
+ this.portRangeCache.set(cacheKey, result);
199
+
200
+ return result;
201
+ }
202
+
203
+ /**
204
+ * Get all ports that should be listened on
205
+ * This method automatically infers all required ports from route configurations
206
+ */
207
+ public getListeningPorts(): number[] {
208
+ // Return the unique set of ports from all routes
209
+ return Array.from(this.portMap.keys());
210
+ }
211
+
212
+ /**
213
+ * Get all routes for a given port
214
+ */
215
+ public getRoutesForPort(port: number): IRouteConfig[] {
216
+ return this.portMap.get(port) || [];
217
+ }
218
+
219
+ /**
220
+ * Find the matching route for a connection
221
+ */
222
+ public findMatchingRoute(context: IRouteContext): IRouteMatchResult | null {
223
+ // Get routes for this port if using port-based filtering
224
+ const routesToCheck = context.port
225
+ ? (this.portMap.get(context.port) || [])
226
+ : this.routes;
227
+
228
+ // Find the first matching route based on priority order
229
+ for (const route of routesToCheck) {
230
+ if (this.matchesRoute(route, context)) {
231
+ return { route };
232
+ }
233
+ }
234
+
235
+ return null;
236
+ }
237
+
238
+ /**
239
+ * Check if a route matches the given context
240
+ */
241
+ private matchesRoute(route: IRouteConfig, context: IRouteContext): boolean {
242
+ // Skip disabled routes
243
+ if (route.enabled === false) {
244
+ return false;
245
+ }
246
+
247
+ // Check port match if provided in context
248
+ if (context.port !== undefined) {
249
+ const ports = this.expandPortRange(route.match.ports);
250
+ if (!ports.includes(context.port)) {
251
+ return false;
252
+ }
253
+ }
254
+
255
+ // Check domain match if specified
256
+ if (route.match.domains && context.domain) {
257
+ const domains = Array.isArray(route.match.domains)
258
+ ? route.match.domains
259
+ : [route.match.domains];
260
+
261
+ if (!domains.some(domainPattern => this.matchDomain(domainPattern, context.domain!))) {
262
+ return false;
263
+ }
264
+ }
265
+
266
+ // Check path match if specified
267
+ if (route.match.path && context.path) {
268
+ if (!this.matchPath(route.match.path, context.path)) {
269
+ return false;
270
+ }
271
+ }
272
+
273
+ // Check client IP match if specified
274
+ if (route.match.clientIp && context.clientIp) {
275
+ if (!route.match.clientIp.some(ip => this.matchIpPattern(ip, context.clientIp))) {
276
+ return false;
277
+ }
278
+ }
279
+
280
+ // Check TLS version match if specified
281
+ if (route.match.tlsVersion && context.tlsVersion) {
282
+ if (!route.match.tlsVersion.includes(context.tlsVersion)) {
283
+ return false;
284
+ }
285
+ }
286
+
287
+ // Check header match if specified
288
+ if (route.match.headers && context.headers) {
289
+ for (const [headerName, expectedValue] of Object.entries(route.match.headers)) {
290
+ const actualValue = context.headers[headerName.toLowerCase()];
291
+
292
+ // If header doesn't exist, no match
293
+ if (actualValue === undefined) {
294
+ return false;
295
+ }
296
+
297
+ // Match against string or regex
298
+ if (typeof expectedValue === 'string') {
299
+ if (actualValue !== expectedValue) {
300
+ return false;
301
+ }
302
+ } else if (expectedValue instanceof RegExp) {
303
+ if (!expectedValue.test(actualValue)) {
304
+ return false;
305
+ }
306
+ }
307
+ }
308
+ }
309
+
310
+ // All criteria matched
311
+ return true;
312
+ }
313
+
314
+ /**
315
+ * Match a domain pattern against a domain
316
+ * @deprecated Use the matchDomain function from route-utils.js instead
317
+ */
318
+ public matchDomain(pattern: string, domain: string): boolean {
319
+ return matchDomain(pattern, domain);
320
+ }
321
+
322
+ /**
323
+ * Match a path pattern against a path
324
+ * @deprecated Use the matchPath function from route-utils.js instead
325
+ */
326
+ public matchPath(pattern: string, path: string): boolean {
327
+ return matchPath(pattern, path);
328
+ }
329
+
330
+ /**
331
+ * Match an IP pattern against a pattern
332
+ * @deprecated Use the matchIpPattern function from route-utils.js instead
333
+ */
334
+ public matchIpPattern(pattern: string, ip: string): boolean {
335
+ return matchIpPattern(pattern, ip);
336
+ }
337
+
338
+ /**
339
+ * Match an IP against a CIDR pattern
340
+ * @deprecated Use the matchIpCidr function from route-utils.js instead
341
+ */
342
+ public matchIpCidr(cidr: string, ip: string): boolean {
343
+ return matchIpCidr(cidr, ip);
344
+ }
345
+
346
+ /**
347
+ * Convert an IP address to a numeric value
348
+ * @deprecated Use the ipToNumber function from route-utils.js instead
349
+ */
350
+ private ipToNumber(ip: string): number {
351
+ return ipToNumber(ip);
352
+ }
353
+
354
+ /**
355
+ * Validate the route configuration and return any warnings
356
+ */
357
+ public validateConfiguration(): string[] {
358
+ const warnings: string[] = [];
359
+ const duplicatePorts = new Map<number, number>();
360
+
361
+ // Check for routes with the same exact match criteria
362
+ for (let i = 0; i < this.routes.length; i++) {
363
+ for (let j = i + 1; j < this.routes.length; j++) {
364
+ const route1 = this.routes[i];
365
+ const route2 = this.routes[j];
366
+
367
+ // Check if route match criteria are the same
368
+ if (this.areMatchesSimilar(route1.match, route2.match)) {
369
+ warnings.push(
370
+ `Routes "${route1.name || i}" and "${route2.name || j}" have similar match criteria. ` +
371
+ `The route with higher priority (${Math.max(route1.priority || 0, route2.priority || 0)}) will be used.`
372
+ );
373
+ }
374
+ }
375
+ }
376
+
377
+ // Check for routes that may never be matched due to priority
378
+ for (let i = 0; i < this.routes.length; i++) {
379
+ const route = this.routes[i];
380
+ const higherPriorityRoutes = this.routes.filter(r =>
381
+ (r.priority || 0) > (route.priority || 0));
382
+
383
+ for (const higherRoute of higherPriorityRoutes) {
384
+ if (this.isRouteShadowed(route, higherRoute)) {
385
+ warnings.push(
386
+ `Route "${route.name || i}" may never be matched because it is shadowed by ` +
387
+ `higher priority route "${higherRoute.name || 'unnamed'}"`
388
+ );
389
+ break;
390
+ }
391
+ }
392
+ }
393
+
394
+ return warnings;
395
+ }
396
+
397
+ /**
398
+ * Check if two route matches are similar (potential conflict)
399
+ */
400
+ private areMatchesSimilar(match1: IRouteMatch, match2: IRouteMatch): boolean {
401
+ // Check port overlap
402
+ const ports1 = new Set(this.expandPortRange(match1.ports));
403
+ const ports2 = new Set(this.expandPortRange(match2.ports));
404
+
405
+ let havePortOverlap = false;
406
+ for (const port of ports1) {
407
+ if (ports2.has(port)) {
408
+ havePortOverlap = true;
409
+ break;
410
+ }
411
+ }
412
+
413
+ if (!havePortOverlap) {
414
+ return false;
415
+ }
416
+
417
+ // Check domain overlap
418
+ if (match1.domains && match2.domains) {
419
+ const domains1 = Array.isArray(match1.domains) ? match1.domains : [match1.domains];
420
+ const domains2 = Array.isArray(match2.domains) ? match2.domains : [match2.domains];
421
+
422
+ // Check if any domain pattern from match1 could match any from match2
423
+ let haveDomainOverlap = false;
424
+ for (const domain1 of domains1) {
425
+ for (const domain2 of domains2) {
426
+ if (domain1 === domain2 ||
427
+ (domain1.includes('*') || domain2.includes('*'))) {
428
+ haveDomainOverlap = true;
429
+ break;
430
+ }
431
+ }
432
+ if (haveDomainOverlap) break;
433
+ }
434
+
435
+ if (!haveDomainOverlap) {
436
+ return false;
437
+ }
438
+ } else if (match1.domains || match2.domains) {
439
+ // One has domains, the other doesn't - they could overlap
440
+ // The one with domains is more specific, so it's not exactly a conflict
441
+ return false;
442
+ }
443
+
444
+ // Check path overlap
445
+ if (match1.path && match2.path) {
446
+ // This is a simplified check - in a real implementation,
447
+ // you'd need to check if the path patterns could match the same paths
448
+ return match1.path === match2.path ||
449
+ match1.path.includes('*') ||
450
+ match2.path.includes('*');
451
+ } else if (match1.path || match2.path) {
452
+ // One has a path, the other doesn't
453
+ return false;
454
+ }
455
+
456
+ // If we get here, the matches have significant overlap
457
+ return true;
458
+ }
459
+
460
+ /**
461
+ * Check if a route is completely shadowed by a higher priority route
462
+ */
463
+ private isRouteShadowed(route: IRouteConfig, higherPriorityRoute: IRouteConfig): boolean {
464
+ // If they don't have similar match criteria, no shadowing occurs
465
+ if (!this.areMatchesSimilar(route.match, higherPriorityRoute.match)) {
466
+ return false;
467
+ }
468
+
469
+ // If higher priority route has more specific criteria, no shadowing
470
+ const routeSpecificity = calculateRouteSpecificity(route.match);
471
+ const higherRouteSpecificity = calculateRouteSpecificity(higherPriorityRoute.match);
472
+
473
+ if (higherRouteSpecificity > routeSpecificity) {
474
+ return false;
475
+ }
476
+
477
+ // If higher priority route is equally or less specific but has higher priority,
478
+ // it shadows the lower priority route
479
+ return true;
480
+ }
481
+
482
+ /**
483
+ * Check if route1 is more specific than route2
484
+ * @deprecated Use the calculateRouteSpecificity function from route-utils.js instead
485
+ */
486
+ private isRouteMoreSpecific(match1: IRouteMatch, match2: IRouteMatch): boolean {
487
+ return calculateRouteSpecificity(match1) > calculateRouteSpecificity(match2);
488
+ }
489
+ }