@push.rocks/smartproxy 28.0.0 → 28.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/changelog.md +30 -0
  2. package/dist_rust/rustproxy_linux_amd64 +0 -0
  3. package/dist_rust/rustproxy_linux_amd64.tsrust-build.json +4 -4
  4. package/dist_rust/rustproxy_linux_arm64 +0 -0
  5. package/dist_rust/rustproxy_linux_arm64.tsrust-build.json +4 -4
  6. package/dist_ts/00_commitinfo_data.js +1 -1
  7. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +4 -2
  8. package/dist_ts/proxies/smart-proxy/models/rust-types.d.ts +17 -0
  9. package/dist_ts/proxies/smart-proxy/models/rust-types.js +3 -2
  10. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +28 -4
  11. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +75 -6
  12. package/dist_ts/proxies/smart-proxy/smart-proxy.js +45 -40
  13. package/dist_ts/proxies/smart-proxy/utils/index.d.ts +2 -0
  14. package/dist_ts/proxies/smart-proxy/utils/index.js +3 -1
  15. package/dist_ts/proxies/smart-proxy/utils/route-validation-error.d.ts +24 -0
  16. package/dist_ts/proxies/smart-proxy/utils/route-validation-error.js +41 -0
  17. package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +12 -2
  18. package/dist_ts/proxies/smart-proxy/utils/route-validator.js +56 -41
  19. package/package.json +2 -2
  20. package/readme.md +32 -3
  21. package/ts/00_commitinfo_data.ts +1 -1
  22. package/ts/proxies/smart-proxy/models/interfaces.ts +4 -2
  23. package/ts/proxies/smart-proxy/models/rust-types.ts +20 -0
  24. package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +106 -13
  25. package/ts/proxies/smart-proxy/smart-proxy.ts +40 -39
  26. package/ts/proxies/smart-proxy/utils/index.ts +4 -0
  27. package/ts/proxies/smart-proxy/utils/route-validation-error.ts +60 -0
  28. package/ts/proxies/smart-proxy/utils/route-validator.ts +74 -49
@@ -1,5 +1,6 @@
1
1
  import { logger } from '../../../core/utils/logger.js';
2
2
  import type { IInboundProxyProtocolPolicy, IRouteConfig, IRouteMatch, IRouteAction, TPortRange, TTransportProtocol } from '../models/route-types.js';
3
+ import { RouteValidationError, type IRouteValidationErrorEntry } from './route-validation-error.js';
3
4
 
4
5
  /**
5
6
  * Validates route configurations for correctness and safety
@@ -323,62 +324,84 @@ export class RouteValidator {
323
324
  }
324
325
 
325
326
  /**
326
- * Validate multiple route configurations
327
+ * Label every route for validation reports: its name, else its id, else `route[<index>]`.
328
+ * A name or id that several routes share gets `[<index>]` appended, so every label is unique.
327
329
  */
328
- public static validateRoutes(routes: IRouteConfig[]): { valid: boolean; errors: Map<string, string[]> } {
329
- const errorMap = new Map<string, string[]>();
330
- let valid = true;
330
+ public static getRouteLabels(routes: IRouteConfig[]): string[] {
331
+ const baseLabels = routes.map((route, index) => route.name || route.id || `route[${index}]`);
332
+ const labelCounts = new Map<string, number>();
333
+ for (const label of baseLabels) {
334
+ labelCounts.set(label, (labelCounts.get(label) ?? 0) + 1);
335
+ }
336
+ return baseLabels.map((label, index) => (labelCounts.get(label)! > 1 ? `${label}[${index}]` : label));
337
+ }
338
+
339
+ /**
340
+ * Validate multiple route configurations.
341
+ *
342
+ * `entries` lists every invalid route by position and label; `errors` maps the same
343
+ * labels to the reasons.
344
+ */
345
+ public static validateRoutes(routes: IRouteConfig[]): {
346
+ valid: boolean;
347
+ errors: Map<string, string[]>;
348
+ entries: IRouteValidationErrorEntry[];
349
+ } {
350
+ const messagesByIndex: string[][] = routes.map(() => []);
331
351
 
332
352
  // Check for duplicate route names
333
353
  const routeNames = new Set<string>();
334
- for (const route of routes) {
354
+ routes.forEach((route, index) => {
335
355
  if (route.name && routeNames.has(route.name)) {
336
- const existingErrors = errorMap.get(route.name) || [];
337
- existingErrors.push('Duplicate route name');
338
- errorMap.set(route.name, existingErrors);
339
- valid = false;
356
+ messagesByIndex[index].push('Duplicate route name');
340
357
  }
341
358
  if (route.name) {
342
359
  routeNames.add(route.name);
343
360
  }
344
- }
361
+ });
345
362
 
346
363
  // Validate each route
347
- for (const route of routes) {
348
- const result = this.validateRoute(route);
349
- if (!result.valid) {
350
- errorMap.set(route.name || 'unnamed', result.errors);
351
- valid = false;
352
- }
353
- }
364
+ routes.forEach((route, index) => {
365
+ messagesByIndex[index].push(...this.validateRoute(route).errors);
366
+ });
367
+
368
+ const labels = this.getRouteLabels(routes);
354
369
 
355
370
  // Check for conflicting routes
356
- const conflicts = this.findRouteConflicts(routes);
357
- if (conflicts.length > 0) {
358
- for (const conflict of conflicts) {
359
- const existingErrors = errorMap.get(conflict.route) || [];
360
- existingErrors.push(conflict.message);
361
- errorMap.set(conflict.route, existingErrors);
362
- }
363
- valid = false;
371
+ for (const conflict of this.findRouteConflicts(routes, labels)) {
372
+ messagesByIndex[conflict.index].push(conflict.message);
364
373
  }
365
374
 
366
- return { valid, errors: errorMap };
375
+ const entries: IRouteValidationErrorEntry[] = [];
376
+ messagesByIndex.forEach((messages, index) => {
377
+ if (messages.length > 0) {
378
+ entries.push({ index, route: labels[index], messages });
379
+ }
380
+ });
381
+
382
+ return {
383
+ valid: entries.length === 0,
384
+ errors: new Map(entries.map((entry) => [entry.route, entry.messages])),
385
+ entries,
386
+ };
367
387
  }
368
388
 
369
389
  /**
370
390
  * Find potential conflicts between routes
371
391
  */
372
- private static findRouteConflicts(routes: IRouteConfig[]): Array<{ route: string; message: string }> {
373
- const conflicts: Array<{ route: string; message: string }> = [];
374
-
375
- // Group routes by port
376
- const portMap = new Map<number, IRouteConfig[]>();
377
-
378
- for (const route of routes) {
392
+ private static findRouteConflicts(
393
+ routes: IRouteConfig[],
394
+ labels: string[],
395
+ ): Array<{ index: number; message: string }> {
396
+ const conflicts: Array<{ index: number; message: string }> = [];
397
+
398
+ // Group route indices by port
399
+ const portMap = new Map<number, number[]>();
400
+
401
+ routes.forEach((route, index) => {
379
402
  if (route.match?.ports) {
380
403
  const ports = Array.isArray(route.match.ports) ? route.match.ports : [route.match.ports];
381
-
404
+
382
405
  // Expand port ranges to individual ports
383
406
  const expandedPorts: number[] = [];
384
407
  for (const port of ports) {
@@ -390,25 +413,25 @@ export class RouteValidator {
390
413
  }
391
414
  }
392
415
  }
393
-
416
+
394
417
  for (const port of expandedPorts) {
395
418
  const routesOnPort = portMap.get(port) || [];
396
- routesOnPort.push(route);
419
+ routesOnPort.push(index);
397
420
  portMap.set(port, routesOnPort);
398
421
  }
399
422
  }
400
- }
423
+ });
401
424
 
402
425
  // Check for conflicting catch-all routes on the same port
403
426
  for (const [port, routesOnPort] of portMap) {
404
- const catchAllRoutes = routesOnPort.filter(r =>
405
- this.isCatchAllDomainMatch(r.match) && !this.hasSourceScope(r.match)
427
+ const catchAllRoutes = routesOnPort.filter((index) =>
428
+ this.isCatchAllDomainMatch(routes[index].match) && !this.hasSourceScope(routes[index].match)
406
429
  );
407
-
430
+
408
431
  if (catchAllRoutes.length > 1) {
409
- for (const route of catchAllRoutes) {
432
+ for (const index of catchAllRoutes) {
410
433
  conflicts.push({
411
- route: route.name || 'unnamed',
434
+ index,
412
435
  message: `Multiple catch-all routes on port ${port}`
413
436
  });
414
437
  }
@@ -419,9 +442,9 @@ export class RouteValidator {
419
442
  // a shorthand for the full port/transport listener policy, so every route
420
443
  // sharing that listener must either all omit it or all use an identical one.
421
444
  const listenerPolicies = new Map<string, { route: string; fingerprint: string }>();
422
- for (const route of routes) {
445
+ routes.forEach((route, index) => {
423
446
  if (!route.match?.ports) {
424
- continue;
447
+ return;
425
448
  }
426
449
 
427
450
  for (const port of this.expandPorts(route.match.ports)) {
@@ -431,18 +454,18 @@ export class RouteValidator {
431
454
  const existing = listenerPolicies.get(key);
432
455
  if (!existing) {
433
456
  listenerPolicies.set(key, {
434
- route: route.name || 'unnamed',
457
+ route: labels[index],
435
458
  fingerprint,
436
459
  });
437
460
  } else if (existing.fingerprint !== fingerprint) {
438
461
  conflicts.push({
439
- route: route.name || 'unnamed',
462
+ index,
440
463
  message: `Conflicting inbound PROXY protocol policy on ${transport} port ${port} (differs from ${existing.route})`,
441
464
  });
442
465
  }
443
466
  }
444
467
  }
445
- }
468
+ });
446
469
 
447
470
  return conflicts;
448
471
  }
@@ -1031,12 +1054,14 @@ export function hasRequiredPropertiesForAction(route: IRouteConfig, actionType:
1031
1054
  * Useful for immediate validation when creating routes
1032
1055
  * @param route Route configuration to validate
1033
1056
  * @returns The validated route configuration
1034
- * @throws Error if the route configuration is invalid
1057
+ * @throws RouteValidationError if the route configuration is invalid
1035
1058
  */
1036
1059
  export function assertValidRoute(route: IRouteConfig): IRouteConfig {
1037
1060
  const validation = validateRouteConfig(route);
1038
1061
  if (!validation.valid) {
1039
- throw new Error(`Invalid route configuration: ${validation.errors.join(', ')}`);
1062
+ throw new RouteValidationError('Invalid route configuration', [
1063
+ { index: 0, route: RouteValidator.getRouteLabels([route])[0], messages: validation.errors },
1064
+ ]);
1040
1065
  }
1041
1066
  return route;
1042
1067
  }