@push.rocks/smartproxy 19.5.19 → 19.5.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/core/models/index.d.ts +2 -0
- package/dist_ts/core/models/index.js +3 -1
- package/dist_ts/core/models/socket-types.d.ts +14 -0
- package/dist_ts/core/models/socket-types.js +15 -0
- package/dist_ts/core/models/wrapped-socket.d.ts +34 -0
- package/dist_ts/core/models/wrapped-socket.js +82 -0
- package/dist_ts/core/routing/index.d.ts +11 -0
- package/dist_ts/core/routing/index.js +17 -0
- package/dist_ts/core/routing/matchers/domain.d.ts +34 -0
- package/dist_ts/core/routing/matchers/domain.js +91 -0
- package/dist_ts/core/routing/matchers/header.d.ts +32 -0
- package/dist_ts/core/routing/matchers/header.js +94 -0
- package/dist_ts/core/routing/matchers/index.d.ts +18 -0
- package/dist_ts/core/routing/matchers/index.js +20 -0
- package/dist_ts/core/routing/matchers/ip.d.ts +53 -0
- package/dist_ts/core/routing/matchers/ip.js +169 -0
- package/dist_ts/core/routing/matchers/path.d.ts +44 -0
- package/dist_ts/core/routing/matchers/path.js +148 -0
- package/dist_ts/core/routing/route-manager.d.ts +88 -0
- package/dist_ts/core/routing/route-manager.js +342 -0
- package/dist_ts/core/routing/route-utils.d.ts +28 -0
- package/dist_ts/core/routing/route-utils.js +67 -0
- package/dist_ts/core/routing/specificity.d.ts +30 -0
- package/dist_ts/core/routing/specificity.js +115 -0
- package/dist_ts/core/routing/types.d.ts +41 -0
- package/dist_ts/core/routing/types.js +5 -0
- package/dist_ts/core/utils/index.d.ts +1 -2
- package/dist_ts/core/utils/index.js +2 -3
- package/dist_ts/core/utils/proxy-protocol.d.ts +45 -0
- package/dist_ts/core/utils/proxy-protocol.js +201 -0
- package/dist_ts/core/utils/route-manager.d.ts +0 -30
- package/dist_ts/core/utils/route-manager.js +6 -47
- package/dist_ts/core/utils/route-utils.d.ts +2 -68
- package/dist_ts/core/utils/route-utils.js +21 -218
- package/dist_ts/core/utils/security-utils.js +4 -4
- package/dist_ts/index.d.ts +2 -5
- package/dist_ts/index.js +5 -11
- package/dist_ts/proxies/http-proxy/http-proxy.d.ts +0 -1
- package/dist_ts/proxies/http-proxy/http-proxy.js +15 -60
- package/dist_ts/proxies/http-proxy/models/types.d.ts +0 -90
- package/dist_ts/proxies/http-proxy/models/types.js +1 -242
- package/dist_ts/proxies/http-proxy/request-handler.d.ts +3 -5
- package/dist_ts/proxies/http-proxy/request-handler.js +20 -171
- package/dist_ts/proxies/http-proxy/websocket-handler.d.ts +2 -5
- package/dist_ts/proxies/http-proxy/websocket-handler.js +15 -23
- package/dist_ts/proxies/index.d.ts +2 -2
- package/dist_ts/proxies/index.js +4 -3
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +3 -1
- package/dist_ts/proxies/smart-proxy/connection-manager.js +17 -7
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +2 -1
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +5 -2
- package/dist_ts/proxies/smart-proxy/index.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/index.js +2 -2
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +7 -2
- package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +1 -0
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +155 -35
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +15 -4
- package/dist_ts/proxies/smart-proxy/utils/route-utils.js +10 -43
- package/dist_ts/routing/router/http-router.d.ts +89 -0
- package/dist_ts/routing/router/http-router.js +205 -0
- package/dist_ts/routing/router/index.d.ts +2 -5
- package/dist_ts/routing/router/index.js +3 -4
- package/package.json +1 -1
- package/readme.delete.md +187 -0
- package/readme.hints.md +196 -1
- package/readme.plan.md +625 -0
- package/readme.proxy-chain-summary.md +112 -0
- package/readme.proxy-protocol-example.md +462 -0
- package/readme.proxy-protocol.md +415 -0
- package/readme.routing.md +341 -0
- package/ts/core/models/index.ts +2 -0
- package/ts/core/models/socket-types.ts +21 -0
- package/ts/core/models/wrapped-socket.ts +99 -0
- package/ts/core/routing/index.ts +21 -0
- package/ts/core/routing/matchers/domain.ts +119 -0
- package/ts/core/routing/matchers/header.ts +120 -0
- package/ts/core/routing/matchers/index.ts +22 -0
- package/ts/core/routing/matchers/ip.ts +207 -0
- package/ts/core/routing/matchers/path.ts +184 -0
- package/ts/core/{utils → routing}/route-manager.ts +7 -57
- package/ts/core/routing/route-utils.ts +88 -0
- package/ts/core/routing/specificity.ts +141 -0
- package/ts/core/routing/types.ts +49 -0
- package/ts/core/utils/index.ts +1 -2
- package/ts/core/utils/proxy-protocol.ts +246 -0
- package/ts/core/utils/security-utils.ts +3 -7
- package/ts/index.ts +4 -14
- package/ts/proxies/http-proxy/http-proxy.ts +13 -68
- package/ts/proxies/http-proxy/models/types.ts +0 -324
- package/ts/proxies/http-proxy/request-handler.ts +15 -186
- package/ts/proxies/http-proxy/websocket-handler.ts +15 -26
- package/ts/proxies/index.ts +3 -2
- package/ts/proxies/smart-proxy/connection-manager.ts +17 -7
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +6 -2
- package/ts/proxies/smart-proxy/index.ts +1 -1
- package/ts/proxies/smart-proxy/models/interfaces.ts +9 -2
- package/ts/proxies/smart-proxy/models/route-types.ts +3 -0
- package/ts/proxies/smart-proxy/route-connection-handler.ts +173 -42
- package/ts/proxies/smart-proxy/smart-proxy.ts +15 -3
- package/ts/proxies/smart-proxy/utils/route-utils.ts +11 -49
- package/ts/routing/router/http-router.ts +266 -0
- package/ts/routing/router/index.ts +3 -8
- package/readme.problems.md +0 -170
- package/ts/core/utils/route-utils.ts +0 -312
- package/ts/proxies/smart-proxy/route-manager.ts +0 -554
- package/ts/routing/router/proxy-router.ts +0 -437
- package/ts/routing/router/route-router.ts +0 -482
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import { matchRouteDomain, calculateRouteSpecificity } from './route-utils.js';
|
|
3
|
+
import { DomainMatcher, PathMatcher, IpMatcher } from './matchers/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Shared RouteManager used by both SmartProxy and NetworkProxy
|
|
6
|
+
*
|
|
7
|
+
* This provides a unified implementation for route management,
|
|
8
|
+
* route matching, and port handling.
|
|
9
|
+
*/
|
|
10
|
+
export class SharedRouteManager extends plugins.EventEmitter {
|
|
11
|
+
constructor(options) {
|
|
12
|
+
super();
|
|
13
|
+
this.routes = [];
|
|
14
|
+
this.portMap = new Map();
|
|
15
|
+
/**
|
|
16
|
+
* Memoization cache for expanded port ranges
|
|
17
|
+
*/
|
|
18
|
+
this.portRangeCache = new Map();
|
|
19
|
+
// Set up logger (use console if not provided)
|
|
20
|
+
this.logger = options.logger || {
|
|
21
|
+
info: console.log,
|
|
22
|
+
warn: console.warn,
|
|
23
|
+
error: console.error,
|
|
24
|
+
debug: options.enableDetailedLogging ? console.log : undefined
|
|
25
|
+
};
|
|
26
|
+
this.enableDetailedLogging = options.enableDetailedLogging || false;
|
|
27
|
+
// Initialize routes if provided
|
|
28
|
+
if (options.routes) {
|
|
29
|
+
this.updateRoutes(options.routes);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Update routes with new configuration
|
|
34
|
+
*/
|
|
35
|
+
updateRoutes(routes = []) {
|
|
36
|
+
// Sort routes by priority (higher first)
|
|
37
|
+
this.routes = [...(routes || [])].sort((a, b) => {
|
|
38
|
+
const priorityA = a.priority ?? 0;
|
|
39
|
+
const priorityB = b.priority ?? 0;
|
|
40
|
+
return priorityB - priorityA;
|
|
41
|
+
});
|
|
42
|
+
// Rebuild port mapping for fast lookups
|
|
43
|
+
this.rebuildPortMap();
|
|
44
|
+
this.logger.info(`Updated RouteManager with ${this.routes.length} routes`);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get all routes
|
|
48
|
+
*/
|
|
49
|
+
getRoutes() {
|
|
50
|
+
return [...this.routes];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Rebuild the port mapping for fast lookups
|
|
54
|
+
* Also logs information about the ports being listened on
|
|
55
|
+
*/
|
|
56
|
+
rebuildPortMap() {
|
|
57
|
+
this.portMap.clear();
|
|
58
|
+
this.portRangeCache.clear(); // Clear cache when rebuilding
|
|
59
|
+
// Track ports for logging
|
|
60
|
+
const portToRoutesMap = new Map();
|
|
61
|
+
for (const route of this.routes) {
|
|
62
|
+
const ports = this.expandPortRange(route.match.ports);
|
|
63
|
+
// Skip if no ports were found
|
|
64
|
+
if (ports.length === 0) {
|
|
65
|
+
this.logger.warn(`Route ${route.name || 'unnamed'} has no valid ports to listen on`);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
for (const port of ports) {
|
|
69
|
+
// Add to portMap for routing
|
|
70
|
+
if (!this.portMap.has(port)) {
|
|
71
|
+
this.portMap.set(port, []);
|
|
72
|
+
}
|
|
73
|
+
this.portMap.get(port).push(route);
|
|
74
|
+
// Add to tracking for logging
|
|
75
|
+
if (!portToRoutesMap.has(port)) {
|
|
76
|
+
portToRoutesMap.set(port, []);
|
|
77
|
+
}
|
|
78
|
+
portToRoutesMap.get(port).push(route.name || 'unnamed');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Log summary of ports and routes
|
|
82
|
+
const totalPorts = this.portMap.size;
|
|
83
|
+
const totalRoutes = this.routes.length;
|
|
84
|
+
this.logger.info(`Route manager configured with ${totalRoutes} routes across ${totalPorts} ports`);
|
|
85
|
+
// Log port details if detailed logging is enabled
|
|
86
|
+
if (this.enableDetailedLogging) {
|
|
87
|
+
for (const [port, routes] of this.portMap.entries()) {
|
|
88
|
+
this.logger.info(`Port ${port}: ${routes.length} routes (${portToRoutesMap.get(port).join(', ')})`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Expand a port range specification into an array of individual ports
|
|
94
|
+
* Uses caching to improve performance for frequently used port ranges
|
|
95
|
+
*
|
|
96
|
+
* @public - Made public to allow external code to interpret port ranges
|
|
97
|
+
*/
|
|
98
|
+
expandPortRange(portRange) {
|
|
99
|
+
// For simple number, return immediately
|
|
100
|
+
if (typeof portRange === 'number') {
|
|
101
|
+
return [portRange];
|
|
102
|
+
}
|
|
103
|
+
// Create a cache key for this port range
|
|
104
|
+
const cacheKey = JSON.stringify(portRange);
|
|
105
|
+
// Check if we have a cached result
|
|
106
|
+
if (this.portRangeCache.has(cacheKey)) {
|
|
107
|
+
return this.portRangeCache.get(cacheKey);
|
|
108
|
+
}
|
|
109
|
+
// Process the port range
|
|
110
|
+
let result = [];
|
|
111
|
+
if (Array.isArray(portRange)) {
|
|
112
|
+
// Handle array of port objects or numbers
|
|
113
|
+
result = portRange.flatMap(item => {
|
|
114
|
+
if (typeof item === 'number') {
|
|
115
|
+
return [item];
|
|
116
|
+
}
|
|
117
|
+
else if (typeof item === 'object' && 'from' in item && 'to' in item) {
|
|
118
|
+
// Handle port range object - check valid range
|
|
119
|
+
if (item.from > item.to) {
|
|
120
|
+
this.logger.warn(`Invalid port range: from (${item.from}) > to (${item.to})`);
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
// Handle port range object
|
|
124
|
+
const ports = [];
|
|
125
|
+
for (let p = item.from; p <= item.to; p++) {
|
|
126
|
+
ports.push(p);
|
|
127
|
+
}
|
|
128
|
+
return ports;
|
|
129
|
+
}
|
|
130
|
+
return [];
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
// Cache the result
|
|
134
|
+
this.portRangeCache.set(cacheKey, result);
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get all ports that should be listened on
|
|
139
|
+
* This method automatically infers all required ports from route configurations
|
|
140
|
+
*/
|
|
141
|
+
getListeningPorts() {
|
|
142
|
+
// Return the unique set of ports from all routes
|
|
143
|
+
return Array.from(this.portMap.keys());
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get all routes for a given port
|
|
147
|
+
*/
|
|
148
|
+
getRoutesForPort(port) {
|
|
149
|
+
return this.portMap.get(port) || [];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Find the matching route for a connection
|
|
153
|
+
*/
|
|
154
|
+
findMatchingRoute(context) {
|
|
155
|
+
// Get routes for this port if using port-based filtering
|
|
156
|
+
const routesToCheck = context.port
|
|
157
|
+
? (this.portMap.get(context.port) || [])
|
|
158
|
+
: this.routes;
|
|
159
|
+
// Find the first matching route based on priority order
|
|
160
|
+
for (const route of routesToCheck) {
|
|
161
|
+
if (this.matchesRoute(route, context)) {
|
|
162
|
+
return { route };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Check if a route matches the given context
|
|
169
|
+
*/
|
|
170
|
+
matchesRoute(route, context) {
|
|
171
|
+
// Skip disabled routes
|
|
172
|
+
if (route.enabled === false) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
// Check port match if provided in context
|
|
176
|
+
if (context.port !== undefined) {
|
|
177
|
+
const ports = this.expandPortRange(route.match.ports);
|
|
178
|
+
if (!ports.includes(context.port)) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Check domain match if specified
|
|
183
|
+
if (route.match.domains && context.domain) {
|
|
184
|
+
const domains = Array.isArray(route.match.domains)
|
|
185
|
+
? route.match.domains
|
|
186
|
+
: [route.match.domains];
|
|
187
|
+
if (!domains.some(domainPattern => DomainMatcher.match(domainPattern, context.domain))) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Check path match if specified
|
|
192
|
+
if (route.match.path && context.path) {
|
|
193
|
+
if (!PathMatcher.match(route.match.path, context.path).matches) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Check client IP match if specified
|
|
198
|
+
if (route.match.clientIp && context.clientIp) {
|
|
199
|
+
if (!route.match.clientIp.some(ip => IpMatcher.match(ip, context.clientIp))) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Check TLS version match if specified
|
|
204
|
+
if (route.match.tlsVersion && context.tlsVersion) {
|
|
205
|
+
if (!route.match.tlsVersion.includes(context.tlsVersion)) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Check header match if specified
|
|
210
|
+
if (route.match.headers && context.headers) {
|
|
211
|
+
for (const [headerName, expectedValue] of Object.entries(route.match.headers)) {
|
|
212
|
+
const actualValue = context.headers[headerName.toLowerCase()];
|
|
213
|
+
// If header doesn't exist, no match
|
|
214
|
+
if (actualValue === undefined) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
// Match against string or regex
|
|
218
|
+
if (typeof expectedValue === 'string') {
|
|
219
|
+
if (actualValue !== expectedValue) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else if (expectedValue instanceof RegExp) {
|
|
224
|
+
if (!expectedValue.test(actualValue)) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// All criteria matched
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Validate the route configuration and return any warnings
|
|
235
|
+
*/
|
|
236
|
+
validateConfiguration() {
|
|
237
|
+
const warnings = [];
|
|
238
|
+
const duplicatePorts = new Map();
|
|
239
|
+
// Check for routes with the same exact match criteria
|
|
240
|
+
for (let i = 0; i < this.routes.length; i++) {
|
|
241
|
+
for (let j = i + 1; j < this.routes.length; j++) {
|
|
242
|
+
const route1 = this.routes[i];
|
|
243
|
+
const route2 = this.routes[j];
|
|
244
|
+
// Check if route match criteria are the same
|
|
245
|
+
if (this.areMatchesSimilar(route1.match, route2.match)) {
|
|
246
|
+
warnings.push(`Routes "${route1.name || i}" and "${route2.name || j}" have similar match criteria. ` +
|
|
247
|
+
`The route with higher priority (${Math.max(route1.priority || 0, route2.priority || 0)}) will be used.`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// Check for routes that may never be matched due to priority
|
|
252
|
+
for (let i = 0; i < this.routes.length; i++) {
|
|
253
|
+
const route = this.routes[i];
|
|
254
|
+
const higherPriorityRoutes = this.routes.filter(r => (r.priority || 0) > (route.priority || 0));
|
|
255
|
+
for (const higherRoute of higherPriorityRoutes) {
|
|
256
|
+
if (this.isRouteShadowed(route, higherRoute)) {
|
|
257
|
+
warnings.push(`Route "${route.name || i}" may never be matched because it is shadowed by ` +
|
|
258
|
+
`higher priority route "${higherRoute.name || 'unnamed'}"`);
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return warnings;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Check if two route matches are similar (potential conflict)
|
|
267
|
+
*/
|
|
268
|
+
areMatchesSimilar(match1, match2) {
|
|
269
|
+
// Check port overlap
|
|
270
|
+
const ports1 = new Set(this.expandPortRange(match1.ports));
|
|
271
|
+
const ports2 = new Set(this.expandPortRange(match2.ports));
|
|
272
|
+
let havePortOverlap = false;
|
|
273
|
+
for (const port of ports1) {
|
|
274
|
+
if (ports2.has(port)) {
|
|
275
|
+
havePortOverlap = true;
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (!havePortOverlap) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
// Check domain overlap
|
|
283
|
+
if (match1.domains && match2.domains) {
|
|
284
|
+
const domains1 = Array.isArray(match1.domains) ? match1.domains : [match1.domains];
|
|
285
|
+
const domains2 = Array.isArray(match2.domains) ? match2.domains : [match2.domains];
|
|
286
|
+
// Check if any domain pattern from match1 could match any from match2
|
|
287
|
+
let haveDomainOverlap = false;
|
|
288
|
+
for (const domain1 of domains1) {
|
|
289
|
+
for (const domain2 of domains2) {
|
|
290
|
+
if (domain1 === domain2 ||
|
|
291
|
+
(domain1.includes('*') || domain2.includes('*'))) {
|
|
292
|
+
haveDomainOverlap = true;
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (haveDomainOverlap)
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
if (!haveDomainOverlap) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
else if (match1.domains || match2.domains) {
|
|
304
|
+
// One has domains, the other doesn't - they could overlap
|
|
305
|
+
// The one with domains is more specific, so it's not exactly a conflict
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
// Check path overlap
|
|
309
|
+
if (match1.path && match2.path) {
|
|
310
|
+
// This is a simplified check - in a real implementation,
|
|
311
|
+
// you'd need to check if the path patterns could match the same paths
|
|
312
|
+
return match1.path === match2.path ||
|
|
313
|
+
match1.path.includes('*') ||
|
|
314
|
+
match2.path.includes('*');
|
|
315
|
+
}
|
|
316
|
+
else if (match1.path || match2.path) {
|
|
317
|
+
// One has a path, the other doesn't
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
// If we get here, the matches have significant overlap
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Check if a route is completely shadowed by a higher priority route
|
|
325
|
+
*/
|
|
326
|
+
isRouteShadowed(route, higherPriorityRoute) {
|
|
327
|
+
// If they don't have similar match criteria, no shadowing occurs
|
|
328
|
+
if (!this.areMatchesSimilar(route.match, higherPriorityRoute.match)) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
// If higher priority route has more specific criteria, no shadowing
|
|
332
|
+
const routeSpecificity = calculateRouteSpecificity(route.match);
|
|
333
|
+
const higherRouteSpecificity = calculateRouteSpecificity(higherPriorityRoute.match);
|
|
334
|
+
if (higherRouteSpecificity > routeSpecificity) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
// If higher priority route is equally or less specific but has higher priority,
|
|
338
|
+
// it shadows the lower priority route
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtbWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL2NvcmUvcm91dGluZy9yb3V0ZS1tYW5hZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFRNUMsT0FBTyxFQUNMLGdCQUFnQixFQUNoQix5QkFBeUIsRUFDMUIsTUFBTSxrQkFBa0IsQ0FBQztBQUMxQixPQUFPLEVBQUUsYUFBYSxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQXFCNUU7Ozs7O0dBS0c7QUFDSCxNQUFNLE9BQU8sa0JBQW1CLFNBQVEsT0FBTyxDQUFDLFlBQVk7SUFXMUQsWUFBWSxPQUlYO1FBQ0MsS0FBSyxFQUFFLENBQUM7UUFmRixXQUFNLEdBQW1CLEVBQUUsQ0FBQztRQUM1QixZQUFPLEdBQWdDLElBQUksR0FBRyxFQUFFLENBQUM7UUFJekQ7O1dBRUc7UUFDSyxtQkFBYyxHQUEwQixJQUFJLEdBQUcsRUFBRSxDQUFDO1FBU3hELDhDQUE4QztRQUM5QyxJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLElBQUk7WUFDOUIsSUFBSSxFQUFFLE9BQU8sQ0FBQyxHQUFHO1lBQ2pCLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSTtZQUNsQixLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7WUFDcEIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsU0FBUztTQUMvRCxDQUFDO1FBRUYsSUFBSSxDQUFDLHFCQUFxQixHQUFHLE9BQU8sQ0FBQyxxQkFBcUIsSUFBSSxLQUFLLENBQUM7UUFFcEUsZ0NBQWdDO1FBQ2hDLElBQUksT0FBTyxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ25CLElBQUksQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxD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
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route matching utilities for SmartProxy components
|
|
3
|
+
*
|
|
4
|
+
* This file provides utility functions that use the unified matchers
|
|
5
|
+
* and additional route-specific utilities.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Match domains from a route against a given domain
|
|
9
|
+
*
|
|
10
|
+
* @param domains Array or single domain pattern to match against
|
|
11
|
+
* @param domain Domain to match
|
|
12
|
+
* @returns Whether the domain matches any of the patterns
|
|
13
|
+
*/
|
|
14
|
+
export declare function matchRouteDomain(domains: string | string[] | undefined, domain: string | undefined): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Calculate route specificity score
|
|
17
|
+
* Higher score means more specific matching criteria
|
|
18
|
+
*
|
|
19
|
+
* @param match Match criteria to evaluate
|
|
20
|
+
* @returns Numeric specificity score
|
|
21
|
+
*/
|
|
22
|
+
export declare function calculateRouteSpecificity(match: {
|
|
23
|
+
domains?: string | string[];
|
|
24
|
+
path?: string;
|
|
25
|
+
clientIp?: string[];
|
|
26
|
+
tlsVersion?: string[];
|
|
27
|
+
headers?: Record<string, string | RegExp>;
|
|
28
|
+
}): number;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route matching utilities for SmartProxy components
|
|
3
|
+
*
|
|
4
|
+
* This file provides utility functions that use the unified matchers
|
|
5
|
+
* and additional route-specific utilities.
|
|
6
|
+
*/
|
|
7
|
+
import { DomainMatcher, PathMatcher, IpMatcher, HeaderMatcher } from './matchers/index.js';
|
|
8
|
+
import { RouteSpecificity } from './specificity.js';
|
|
9
|
+
/**
|
|
10
|
+
* Match domains from a route against a given domain
|
|
11
|
+
*
|
|
12
|
+
* @param domains Array or single domain pattern to match against
|
|
13
|
+
* @param domain Domain to match
|
|
14
|
+
* @returns Whether the domain matches any of the patterns
|
|
15
|
+
*/
|
|
16
|
+
export function matchRouteDomain(domains, domain) {
|
|
17
|
+
// If no domains specified in the route, match all domains
|
|
18
|
+
if (!domains) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
// If no domain in the request, can't match domain-specific routes
|
|
22
|
+
if (!domain) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const patterns = Array.isArray(domains) ? domains : [domains];
|
|
26
|
+
return patterns.some(pattern => DomainMatcher.match(pattern, domain));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Calculate route specificity score
|
|
30
|
+
* Higher score means more specific matching criteria
|
|
31
|
+
*
|
|
32
|
+
* @param match Match criteria to evaluate
|
|
33
|
+
* @returns Numeric specificity score
|
|
34
|
+
*/
|
|
35
|
+
export function calculateRouteSpecificity(match) {
|
|
36
|
+
let score = 0;
|
|
37
|
+
// Path specificity using PathMatcher
|
|
38
|
+
if (match.path) {
|
|
39
|
+
score += PathMatcher.calculateSpecificity(match.path);
|
|
40
|
+
}
|
|
41
|
+
// Domain specificity using DomainMatcher
|
|
42
|
+
if (match.domains) {
|
|
43
|
+
const domains = Array.isArray(match.domains) ? match.domains : [match.domains];
|
|
44
|
+
// Use the highest specificity among all domains
|
|
45
|
+
const domainScore = Math.max(...domains.map(d => DomainMatcher.calculateSpecificity(d)));
|
|
46
|
+
score += domainScore;
|
|
47
|
+
}
|
|
48
|
+
// Headers specificity using HeaderMatcher
|
|
49
|
+
if (match.headers) {
|
|
50
|
+
const stringHeaders = {};
|
|
51
|
+
for (const [key, value] of Object.entries(match.headers)) {
|
|
52
|
+
stringHeaders[key] = value instanceof RegExp ? value.source : value;
|
|
53
|
+
}
|
|
54
|
+
score += HeaderMatcher.calculateSpecificity(stringHeaders);
|
|
55
|
+
}
|
|
56
|
+
// Client IP adds some specificity
|
|
57
|
+
if (match.clientIp && match.clientIp.length > 0) {
|
|
58
|
+
// Use the first IP pattern for specificity
|
|
59
|
+
score += IpMatcher.calculateSpecificity(match.clientIp[0]);
|
|
60
|
+
}
|
|
61
|
+
// TLS version adds minimal specificity
|
|
62
|
+
if (match.tlsVersion && match.tlsVersion.length > 0) {
|
|
63
|
+
score += match.tlsVersion.length * 10;
|
|
64
|
+
}
|
|
65
|
+
return score;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtdXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jb3JlL3JvdXRpbmcvcm91dGUtdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0dBS0c7QUFFSCxPQUFPLEVBQUUsYUFBYSxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDM0YsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFLcEQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLGdCQUFnQixDQUFDLE9BQXNDLEVBQUUsTUFBMEI7SUFDakcsMERBQTBEO0lBQzFELElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNiLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELGtFQUFrRTtJQUNsRSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDWixPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRCxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDOUQsT0FBTyxRQUFRLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztBQUN4RSxDQUFDO0FBSUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLHlCQUF5QixDQUFDLEtBTXpDO0lBQ0MsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO0lBRWQscUNBQXFDO0lBQ3JDLElBQUksS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ2YsS0FBSyxJQUFJLFdBQVcsQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDeEQsQ0FBQztJQUVELHlDQUF5QztJQUN6QyxJQUFJLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNsQixNQUFNLE9BQU8sR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDL0UsZ0RBQWdEO1FBQ2hELE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsYUFBYSxDQUFDLG9CQUFvQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN6RixLQUFLLElBQUksV0FBVyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCwwQ0FBMEM7SUFDMUMsSUFBSSxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDbEIsTUFBTSxhQUFhLEdBQTJCLEVBQUUsQ0FBQztRQUNqRCxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUN6RCxhQUFhLENBQUMsR0FBRyxDQUFDLEdBQUcsS0FBSyxZQUFZLE1BQU0sQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDO1FBQ3RFLENBQUM7UUFDRCxLQUFLLElBQUksYUFBYSxDQUFDLG9CQUFvQixDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQzdELENBQUM7SUFFRCxrQ0FBa0M7SUFDbEMsSUFBSSxLQUFLLENBQUMsUUFBUSxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ2hELDJDQUEyQztRQUMzQyxLQUFLLElBQUksU0FBUyxDQUFDLG9CQUFvQixDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM3RCxDQUFDO0lBRUQsdUNBQXVDO0lBQ3ZDLElBQUksS0FBSyxDQUFDLFVBQVUsSUFBSSxLQUFLLENBQUMsVUFBVSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNwRCxLQUFLLElBQUksS0FBSyxDQUFDLFVBQVUsQ0FBQyxNQUFNLEdBQUcsRUFBRSxDQUFDO0lBQ3hDLENBQUM7SUFFRCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUMifQ==
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { IRouteConfig } from '../../proxies/smart-proxy/models/route-types.js';
|
|
2
|
+
import type { IRouteSpecificity } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Unified route specificity calculator
|
|
5
|
+
* Provides consistent specificity scoring across all routing components
|
|
6
|
+
*/
|
|
7
|
+
export declare class RouteSpecificity {
|
|
8
|
+
/**
|
|
9
|
+
* Calculate the total specificity score for a route
|
|
10
|
+
* Higher scores indicate more specific routes that should match first
|
|
11
|
+
*/
|
|
12
|
+
static calculate(route: IRouteConfig): IRouteSpecificity;
|
|
13
|
+
/**
|
|
14
|
+
* Compare two routes and determine which is more specific
|
|
15
|
+
* @returns positive if route1 is more specific, negative if route2 is more specific, 0 if equal
|
|
16
|
+
*/
|
|
17
|
+
static compare(route1: IRouteConfig, route2: IRouteConfig): number;
|
|
18
|
+
/**
|
|
19
|
+
* Sort routes by specificity (most specific first)
|
|
20
|
+
*/
|
|
21
|
+
static sort(routes: IRouteConfig[]): IRouteConfig[];
|
|
22
|
+
/**
|
|
23
|
+
* Find the most specific route from a list
|
|
24
|
+
*/
|
|
25
|
+
static findMostSpecific(routes: IRouteConfig[]): IRouteConfig | null;
|
|
26
|
+
/**
|
|
27
|
+
* Check if a route has any matching criteria
|
|
28
|
+
*/
|
|
29
|
+
static hasMatchCriteria(route: IRouteConfig): boolean;
|
|
30
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { DomainMatcher, PathMatcher, IpMatcher, HeaderMatcher } from './matchers/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Unified route specificity calculator
|
|
4
|
+
* Provides consistent specificity scoring across all routing components
|
|
5
|
+
*/
|
|
6
|
+
export class RouteSpecificity {
|
|
7
|
+
/**
|
|
8
|
+
* Calculate the total specificity score for a route
|
|
9
|
+
* Higher scores indicate more specific routes that should match first
|
|
10
|
+
*/
|
|
11
|
+
static calculate(route) {
|
|
12
|
+
const specificity = {
|
|
13
|
+
pathSpecificity: 0,
|
|
14
|
+
domainSpecificity: 0,
|
|
15
|
+
ipSpecificity: 0,
|
|
16
|
+
headerSpecificity: 0,
|
|
17
|
+
tlsSpecificity: 0,
|
|
18
|
+
totalScore: 0
|
|
19
|
+
};
|
|
20
|
+
// Path specificity
|
|
21
|
+
if (route.match.path) {
|
|
22
|
+
specificity.pathSpecificity = PathMatcher.calculateSpecificity(route.match.path);
|
|
23
|
+
}
|
|
24
|
+
// Domain specificity
|
|
25
|
+
if (route.match.domains) {
|
|
26
|
+
const domains = Array.isArray(route.match.domains)
|
|
27
|
+
? route.match.domains
|
|
28
|
+
: [route.match.domains];
|
|
29
|
+
// Use the highest specificity among all domains
|
|
30
|
+
specificity.domainSpecificity = Math.max(...domains.map(d => DomainMatcher.calculateSpecificity(d)));
|
|
31
|
+
}
|
|
32
|
+
// IP specificity (clientIp is an array of IPs)
|
|
33
|
+
if (route.match.clientIp && route.match.clientIp.length > 0) {
|
|
34
|
+
// Use the first IP pattern for specificity calculation
|
|
35
|
+
specificity.ipSpecificity = IpMatcher.calculateSpecificity(route.match.clientIp[0]);
|
|
36
|
+
}
|
|
37
|
+
// Header specificity (convert RegExp values to strings)
|
|
38
|
+
if (route.match.headers) {
|
|
39
|
+
const stringHeaders = {};
|
|
40
|
+
for (const [key, value] of Object.entries(route.match.headers)) {
|
|
41
|
+
stringHeaders[key] = value instanceof RegExp ? value.source : value;
|
|
42
|
+
}
|
|
43
|
+
specificity.headerSpecificity = HeaderMatcher.calculateSpecificity(stringHeaders);
|
|
44
|
+
}
|
|
45
|
+
// TLS version specificity
|
|
46
|
+
if (route.match.tlsVersion && route.match.tlsVersion.length > 0) {
|
|
47
|
+
specificity.tlsSpecificity = route.match.tlsVersion.length * 10;
|
|
48
|
+
}
|
|
49
|
+
// Calculate total score with weights
|
|
50
|
+
specificity.totalScore =
|
|
51
|
+
specificity.pathSpecificity * 3 + // Path is most important
|
|
52
|
+
specificity.domainSpecificity * 2 + // Domain is second
|
|
53
|
+
specificity.ipSpecificity * 1.5 + // IP is moderately important
|
|
54
|
+
specificity.headerSpecificity * 1 + // Headers are less important
|
|
55
|
+
specificity.tlsSpecificity * 0.5; // TLS is least important
|
|
56
|
+
return specificity;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Compare two routes and determine which is more specific
|
|
60
|
+
* @returns positive if route1 is more specific, negative if route2 is more specific, 0 if equal
|
|
61
|
+
*/
|
|
62
|
+
static compare(route1, route2) {
|
|
63
|
+
const spec1 = this.calculate(route1);
|
|
64
|
+
const spec2 = this.calculate(route2);
|
|
65
|
+
// First compare by total score
|
|
66
|
+
if (spec1.totalScore !== spec2.totalScore) {
|
|
67
|
+
return spec1.totalScore - spec2.totalScore;
|
|
68
|
+
}
|
|
69
|
+
// If total scores are equal, compare by individual components
|
|
70
|
+
// Path is most important tiebreaker
|
|
71
|
+
if (spec1.pathSpecificity !== spec2.pathSpecificity) {
|
|
72
|
+
return spec1.pathSpecificity - spec2.pathSpecificity;
|
|
73
|
+
}
|
|
74
|
+
// Then domain
|
|
75
|
+
if (spec1.domainSpecificity !== spec2.domainSpecificity) {
|
|
76
|
+
return spec1.domainSpecificity - spec2.domainSpecificity;
|
|
77
|
+
}
|
|
78
|
+
// Then IP
|
|
79
|
+
if (spec1.ipSpecificity !== spec2.ipSpecificity) {
|
|
80
|
+
return spec1.ipSpecificity - spec2.ipSpecificity;
|
|
81
|
+
}
|
|
82
|
+
// Then headers
|
|
83
|
+
if (spec1.headerSpecificity !== spec2.headerSpecificity) {
|
|
84
|
+
return spec1.headerSpecificity - spec2.headerSpecificity;
|
|
85
|
+
}
|
|
86
|
+
// Finally TLS
|
|
87
|
+
return spec1.tlsSpecificity - spec2.tlsSpecificity;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Sort routes by specificity (most specific first)
|
|
91
|
+
*/
|
|
92
|
+
static sort(routes) {
|
|
93
|
+
return [...routes].sort((a, b) => this.compare(b, a));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Find the most specific route from a list
|
|
97
|
+
*/
|
|
98
|
+
static findMostSpecific(routes) {
|
|
99
|
+
if (routes.length === 0)
|
|
100
|
+
return null;
|
|
101
|
+
return routes.reduce((most, current) => this.compare(current, most) > 0 ? current : most);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Check if a route has any matching criteria
|
|
105
|
+
*/
|
|
106
|
+
static hasMatchCriteria(route) {
|
|
107
|
+
const match = route.match;
|
|
108
|
+
return !!(match.domains ||
|
|
109
|
+
match.path ||
|
|
110
|
+
match.clientIp?.length ||
|
|
111
|
+
match.headers ||
|
|
112
|
+
match.tlsVersion?.length);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BlY2lmaWNpdHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jb3JlL3JvdXRpbmcvc3BlY2lmaWNpdHkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsT0FBTyxFQUFFLGFBQWEsRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRTNGOzs7R0FHRztBQUNILE1BQU0sT0FBTyxnQkFBZ0I7SUFDM0I7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLFNBQVMsQ0FBQyxLQUFtQjtRQUNsQyxNQUFNLFdBQVcsR0FBc0I7WUFDckMsZUFBZSxFQUFFLENBQUM7WUFDbEIsaUJBQWlCLEVBQUUsQ0FBQztZQUNwQixhQUFhLEVBQUUsQ0FBQztZQUNoQixpQkFBaUIsRUFBRSxDQUFDO1lBQ3BCLGNBQWMsRUFBRSxDQUFDO1lBQ2pCLFVBQVUsRUFBRSxDQUFDO1NBQ2QsQ0FBQztRQUVGLG1CQUFtQjtRQUNuQixJQUFJLEtBQUssQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDckIsV0FBVyxDQUFDLGVBQWUsR0FBRyxXQUFXLENBQUMsb0JBQW9CLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNuRixDQUFDO1FBRUQscUJBQXFCO1FBQ3JCLElBQUksS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUN4QixNQUFNLE9BQU8sR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDO2dCQUNoRCxDQUFDLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUFPO2dCQUNyQixDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBRTFCLGdEQUFnRDtZQUNoRCxXQUFXLENBQUMsaUJBQWlCLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FDdEMsR0FBRyxPQUFPLENBQUMs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
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core routing types used throughout the routing system
|
|
3
|
+
*/
|
|
4
|
+
export interface IPathMatchResult {
|
|
5
|
+
matches: boolean;
|
|
6
|
+
params?: Record<string, string>;
|
|
7
|
+
pathMatch?: string;
|
|
8
|
+
pathRemainder?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface IRouteMatchResult {
|
|
11
|
+
matches: boolean;
|
|
12
|
+
score: number;
|
|
13
|
+
specificity: number;
|
|
14
|
+
matchedCriteria: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface IDomainMatchOptions {
|
|
17
|
+
allowWildcards?: boolean;
|
|
18
|
+
caseInsensitive?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface IIpMatchOptions {
|
|
21
|
+
allowCidr?: boolean;
|
|
22
|
+
allowRanges?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface IHeaderMatchOptions {
|
|
25
|
+
caseInsensitive?: boolean;
|
|
26
|
+
exactMatch?: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface IRouteSpecificity {
|
|
29
|
+
pathSpecificity: number;
|
|
30
|
+
domainSpecificity: number;
|
|
31
|
+
ipSpecificity: number;
|
|
32
|
+
headerSpecificity: number;
|
|
33
|
+
tlsSpecificity: number;
|
|
34
|
+
totalScore: number;
|
|
35
|
+
}
|
|
36
|
+
export interface IMatcher<T = any, O = any> {
|
|
37
|
+
match(pattern: string, value: string, options?: O): T | boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface IAsyncMatcher<T = any, O = any> {
|
|
40
|
+
match(pattern: string, value: string, options?: O): Promise<T | boolean>;
|
|
41
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core routing types used throughout the routing system
|
|
3
|
+
*/
|
|
4
|
+
export {};
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jb3JlL3JvdXRpbmcvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUcifQ==
|