@push.rocks/smartproxy 27.22.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 (33) hide show
  1. package/changelog.md +47 -0
  2. package/dist_rust/rustproxy_linux_amd64 +0 -0
  3. package/dist_rust/rustproxy_linux_amd64.tsrust-build.json +5 -5
  4. package/dist_rust/rustproxy_linux_arm64 +0 -0
  5. package/dist_rust/rustproxy_linux_arm64.tsrust-build.json +5 -5
  6. package/dist_ts/00_commitinfo_data.js +2 -2
  7. package/dist_ts/index.d.ts +1 -1
  8. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +4 -2
  9. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +12 -6
  10. package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
  11. package/dist_ts/proxies/smart-proxy/models/rust-types.d.ts +17 -0
  12. package/dist_ts/proxies/smart-proxy/models/rust-types.js +3 -2
  13. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +28 -4
  14. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +75 -6
  15. package/dist_ts/proxies/smart-proxy/smart-proxy.js +46 -41
  16. package/dist_ts/proxies/smart-proxy/utils/index.d.ts +2 -0
  17. package/dist_ts/proxies/smart-proxy/utils/index.js +3 -1
  18. package/dist_ts/proxies/smart-proxy/utils/route-validation-error.d.ts +24 -0
  19. package/dist_ts/proxies/smart-proxy/utils/route-validation-error.js +41 -0
  20. package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +14 -2
  21. package/dist_ts/proxies/smart-proxy/utils/route-validator.js +178 -43
  22. package/package.json +3 -3
  23. package/readme.md +74 -4
  24. package/ts/00_commitinfo_data.ts +1 -1
  25. package/ts/index.ts +1 -1
  26. package/ts/proxies/smart-proxy/models/interfaces.ts +4 -2
  27. package/ts/proxies/smart-proxy/models/route-types.ts +14 -6
  28. package/ts/proxies/smart-proxy/models/rust-types.ts +20 -0
  29. package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +106 -13
  30. package/ts/proxies/smart-proxy/smart-proxy.ts +41 -40
  31. package/ts/proxies/smart-proxy/utils/index.ts +4 -0
  32. package/ts/proxies/smart-proxy/utils/route-validation-error.ts +60 -0
  33. package/ts/proxies/smart-proxy/utils/route-validator.ts +208 -51
package/changelog.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026-09-25 - 28.1.0
4
+
5
+ ### Features
6
+
7
+ - throw `RouteValidationError` with the per-route reasons when SmartProxy refuses a route set (routes)
8
+ - The constructor and `updateRoutes()` threw a plain `Error` that only counted the invalid routes, for example `Route validation failed: 1 route(s) have errors`; the reasons, such as `response.body requires an explicit content-type header`, went only to the log. A consumer that relays the error could neither show why a route was refused nor tell a refusal from an internal failure.
9
+ - `RouteValidationError` (exported with its entry type `IRouteValidationErrorEntry`) has the name `RouteValidationError` and an `errors` list with every rejected route: its `index` in the passed list, its `route` label and all its reasons. Its message names the first 10 routes with up to 5 reasons each, shortens long labels and reasons to 200 characters and counts the rest, so a large route set cannot produce an unbounded message.
10
+ - The same error comes from the TypeScript validation in the constructor and `updateRoutes()`, from challenge route validation in `start()` and `updateRoutes()`, and from `assertValidRoute()`. Challenge reasons no longer repeat the route name, which is now the entry's `route`.
11
+ - Checks that only the Rust engine makes, such as duplicate route ids or an `inboundProxyProtocol` policy without route-level or global `trustedProxyIPs`, reach `start()` and `updateRoutes()` as the same error. The engine answers a refused `start` or `updateRoutes` command with `errorCode: 'ROUTE_VALIDATION'` and the rejected routes in `errorData.routes`.
12
+ - The error is still an `Error`; only the message text changes.
13
+ - add `RouteValidator.getRouteLabels()` (routes)
14
+ - It labels each route by its name, else its id, else `route[<index>]`, and appends `[<index>]` to a name or id that several routes share. `RouteValidator.validateRoutes()` returns its reasons per route as a new `entries` list; route refusals from the Rust engine carry the same labels, matched to the sent routes by index.
15
+
16
+ ### Fixes
17
+
18
+ - keep the reasons of every rejected route (routes)
19
+ - `RouteValidator.validateRoutes()` keyed its error map by `name || 'unnamed'`, so unnamed routes overwrote each other's reasons, and a route's own errors replaced the `Duplicate route name` error of a route with the same name. The map is now keyed by the unique labels above.
20
+ - The Rust engine refused an invalid route set at start with only a count (`Route validation failed with N errors`); it now reports every rejected route with its reasons.
21
+
22
+ - use `rustBinaryPath` (process)
23
+ - `ISmartProxyOptions.rustBinaryPath` was declared and documented as the first place to look for the Rust binary, but SmartProxy never passed it on and always located the binary by discovery.
24
+ - It is now the only candidate when set. A missing or non-executable path makes `start()` reject with a `RustBinaryLocatorError` naming the path, without falling back to discovery.
25
+
26
+ ### Maintenance
27
+
28
+ - update `@push.rocks/smartrust` from `^1.9.0` to `^2.4.0` (dependencies)
29
+ - 2.4.0 carries the structured `errorData` of Rust error responses as `RustBridgeRequestError.responseErrorData`. The breaking change in 2.0.0, strict validation of an explicit `binaryPath`, applies to `rustBinaryPath` as described above; discovery through the environment variable, the platform packages and local paths is unchanged.
30
+ - regenerate `deno.lock` (tooling)
31
+ - The lockfile still listed the development tooling of 27.x (`tstest` 4, `tsbuild` 4) and pointed every package at the Verdaccio proxy through `tarball` URLs, which Deno 2.9 rejects when it installs against npmjs. It now matches `package.json` and carries no `tarball` URLs; `deno install --frozen` against npmjs succeeds with it.
32
+
33
+ ## 2026-09-25 - 28.0.0
34
+
35
+ ### Breaking Changes
36
+
37
+ - remove `action.advanced.testResponse` and the `IRouteTestResponse` type (routes)
38
+ - A route whose `advanced.testResponse` was set answered every request with that response, but only after the TypeScript validator had demanded a forward target and only when the connection reached the HTTP layer; a route without a target was closed before TLS or HTTP. The Rust handler also turned an invalid status into 200 and dropped invalid headers without a word.
39
+ - Use a route with `action.type: 'respond'` and `action.response` instead. The readme has a migration note.
40
+
41
+ ### Features
42
+
43
+ - add the `respond` route action for fixed HTTP responses without an upstream (routes)
44
+ - `action.response` (exported type `IRouteFixedResponse`) sets `status` (200–599), `headers` and an optional UTF-8 `body` of at most 64 KiB; the limit is exported as `MAX_FIXED_RESPONSE_BODY_BYTES`. A non-empty body needs an explicit `content-type` header; 204 and 304 carry no body; hop-by-hop and computed headers (`connection`, `keep-alive`, `transfer-encoding`, `upgrade`, `te`, `trailer`, `proxy-connection`, `content-length`) are rejected. Both the TypeScript validators and the Rust route validation reject an invalid response, and SmartProxy refuses to start or update routes with one.
45
+ - A respond route answers plain HTTP, or HTTPS with `tls.mode: 'terminate'` and a static or `'auto'` certificate, over HTTP/1.1 and h2. Route security, rate limits, challenges and `maxConnections` apply before the response. Non-HTTP connections to a respond route are closed.
46
+ - Respond routes do not support UDP or HTTP/3 yet: `action.udp`, `match.transport` `'udp'` or `'all'` and any `match.protocol` other than `'http'` are rejected. `targets`, socket and datagram handlers and nftables forwarding are rejected as well.
47
+ - `getEligibleDomainsForCertificates()` includes the domains of respond routes with `certificate: 'auto'`.
48
+ - The Rust TCP listener accepts TLS, detects HTTP and hands the connection to the HTTP proxy through one shared path for `terminate`, `terminate-and-reencrypt` and respond routes; forward routes behave as before.
49
+
3
50
  ## 2026-09-24 - 27.22.0
4
51
 
5
52
  ### Features
Binary file
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "format": "tsrust.build-provenance.v2",
3
- "binarySha256": "871c23a5d1c35688b1a36fc106ec385889d4d7d3cefaae0e96e9f46838af1f0c",
3
+ "binarySha256": "215b6a6f4376e8db17e92423ee26fc904b18e4dcbd7a4f45fa278d9ff751a77e",
4
4
  "buildInfo": {
5
5
  "projectName": "@push.rocks/smartproxy",
6
- "projectVersion": "27.22.0",
7
- "gitCommit": "d3ec0ed757055b8c34faa3832c37e0d5eb041f79",
6
+ "projectVersion": "28.1.0",
7
+ "gitCommit": "1366dbee20ee8d15391225a72e3ccf0bd9e1a393",
8
8
  "gitDirty": false,
9
- "builtAt": "2026-09-24T10:06:07.234Z",
10
- "tsrustVersion": "1.13.2",
9
+ "builtAt": "2026-09-25T09:08:38.732Z",
10
+ "tsrustVersion": "1.14.0",
11
11
  "binary": "rustproxy",
12
12
  "target": "linux_amd64"
13
13
  }
Binary file
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "format": "tsrust.build-provenance.v2",
3
- "binarySha256": "1cb97ab1d316f25928125fc94d7a750da7b74d57821c9dee89311f472959b739",
3
+ "binarySha256": "51cd3a5a0830160c897692216038967970fa8375253d2e62819125085a006fa9",
4
4
  "buildInfo": {
5
5
  "projectName": "@push.rocks/smartproxy",
6
- "projectVersion": "27.22.0",
7
- "gitCommit": "d3ec0ed757055b8c34faa3832c37e0d5eb041f79",
6
+ "projectVersion": "28.1.0",
7
+ "gitCommit": "1366dbee20ee8d15391225a72e3ccf0bd9e1a393",
8
8
  "gitDirty": false,
9
- "builtAt": "2026-09-24T10:07:08.117Z",
10
- "tsrustVersion": "1.13.2",
9
+ "builtAt": "2026-09-25T09:09:38.815Z",
10
+ "tsrustVersion": "1.14.0",
11
11
  "binary": "rustproxy",
12
12
  "target": "linux_arm64"
13
13
  }
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartproxy',
6
- version: '27.22.0',
6
+ version: '28.1.0',
7
7
  description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
8
8
  };
9
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFNBQVM7SUFDbEIsV0FBVyxFQUFFLHFQQUFxUDtDQUNuUSxDQUFBIn0=
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLHFQQUFxUDtDQUNuUSxDQUFBIn0=
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export { SmartProxy } from './proxies/smart-proxy/index.js';
5
5
  export { SharedRouteManager as RouteManager } from './core/routing/route-manager.js';
6
- export type { ISmartProxyOptions, ISmartProxySecurityPolicy, ISmartProxyChallengeOptions, IConnectionRecord, IRouteConfig, IRouteMatch, IRouteAction, IRouteTls, IRouteContext, TDatagramHandler, IDatagramInfo, IActiveConnectionSnapshot, IActiveConnectionSnapshotOptions, IActiveConnectionSnapshotPage } from './proxies/smart-proxy/models/index.js';
6
+ export type { ISmartProxyOptions, ISmartProxySecurityPolicy, ISmartProxyChallengeOptions, IConnectionRecord, IRouteConfig, IRouteMatch, IRouteAction, IRouteFixedResponse, IRouteTls, IRouteContext, TDatagramHandler, IDatagramInfo, IActiveConnectionSnapshot, IActiveConnectionSnapshotOptions, IActiveConnectionSnapshotPage } from './proxies/smart-proxy/models/index.js';
7
7
  export type { TSmartProxyCertProvisionObject, ICertProvisionEventComms, ICertificateIssuedEvent, ICertificateFailedEvent } from './proxies/smart-proxy/models/interfaces.js';
8
8
  export * from './proxies/smart-proxy/utils/index.js';
9
9
  export * from './core/models/common-types.js';
@@ -247,8 +247,10 @@ export interface ISmartProxyOptions {
247
247
  */
248
248
  certStore?: ISmartProxyCertStore;
249
249
  /**
250
- * Path to the RustProxy binary. If not set, the binary is located
251
- * automatically via env var, platform package, local build, or PATH.
250
+ * Path to the RustProxy binary. When set, it is the only candidate: `start()` rejects
251
+ * with a `RustBinaryLocatorError` if it is missing or not executable, without falling
252
+ * back to discovery. If not set, the binary is located automatically via env var,
253
+ * platform package, local build, or PATH.
252
254
  */
253
255
  rustBinaryPath?: string;
254
256
  }
@@ -4,7 +4,7 @@ export type { IRouteContext };
4
4
  /**
5
5
  * Supported action types for route configurations
6
6
  */
7
- export type TRouteActionType = 'forward' | 'socket-handler';
7
+ export type TRouteActionType = 'forward' | 'socket-handler' | 'respond';
8
8
  /**
9
9
  * Socket handler function type
10
10
  */
@@ -184,12 +184,18 @@ export interface IRouteStaticFiles {
184
184
  disableDirectoryListing?: boolean;
185
185
  }
186
186
  /**
187
- * Test route response configuration
187
+ * Fixed response answered by a `respond` route without contacting any upstream.
188
+ *
189
+ * - `status` must be an integer from 200 to 599.
190
+ * - `headers` must not contain hop-by-hop or computed headers (`connection`, `keep-alive`,
191
+ * `transfer-encoding`, `upgrade`, `te`, `trailer`, `proxy-connection`, `content-length`).
192
+ * - A non-empty `body` requires an explicit `content-type` header; 204 and 304 carry no body.
193
+ * - `body` is UTF-8 text of at most 65536 bytes.
188
194
  */
189
- export interface IRouteTestResponse {
195
+ export interface IRouteFixedResponse {
190
196
  status: number;
191
- headers: Record<string, string>;
192
- body: string;
197
+ headers?: Record<string, string>;
198
+ body?: string;
193
199
  }
194
200
  /**
195
201
  * URL rewriting configuration
@@ -208,7 +214,6 @@ export interface IRouteAdvanced {
208
214
  headers?: Record<string, string>;
209
215
  keepAlive?: boolean;
210
216
  staticFiles?: IRouteStaticFiles;
211
- testResponse?: IRouteTestResponse;
212
217
  urlRewrite?: IRouteUrlRewrite;
213
218
  }
214
219
  /**
@@ -254,6 +259,7 @@ export interface IRouteAction {
254
259
  };
255
260
  forwardingEngine?: 'node' | 'nftables';
256
261
  nftables?: INfTablesOptions;
262
+ response?: IRouteFixedResponse;
257
263
  socketHandler?: TSocketHandler;
258
264
  datagramHandler?: TDatagramHandler;
259
265
  sendProxyProtocol?: boolean;
@@ -1,3 +1,3 @@
1
1
  import * as plugins from '../../../plugins.js';
2
2
  // Configuration moved to models/interfaces.ts as ISmartProxyOptions
3
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtdHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L21vZGVscy9yb3V0ZS10eXBlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHFCQUFxQixDQUFDO0FBb2UvQyxvRUFBb0UifQ==
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtdHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L21vZGVscy9yb3V0ZS10eXBlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHFCQUFxQixDQUFDO0FBNGUvQyxvRUFBb0UifQ==
@@ -27,6 +27,23 @@ export interface IRustRouteConfig extends Omit<IRouteConfig, 'match' | 'action'>
27
27
  match: IRustRouteMatch;
28
28
  action: IRustRouteAction;
29
29
  }
30
+ /** `errorCode` of a Rust `start` or `updateRoutes` response that refused its routes in validation. */
31
+ export declare const RUST_ROUTE_VALIDATION_ERROR_CODE = "ROUTE_VALIDATION";
32
+ /** One rejected route in the `errorData` of a `ROUTE_VALIDATION` response. */
33
+ export interface IRustRouteValidationIssue {
34
+ /** The route's position in the sent route list. */
35
+ index: number;
36
+ /**
37
+ * The engine's label of the route as it received it. The preprocessor names unnamed routes
38
+ * `route_<index>`, so TypeScript relabels issues by `index` and does not surface this label.
39
+ */
40
+ route: string;
41
+ messages: string[];
42
+ }
43
+ /** `errorData` of a `ROUTE_VALIDATION` response. */
44
+ export interface IRustRouteValidationErrorData {
45
+ routes: IRustRouteValidationIssue[];
46
+ }
30
47
  export interface IRustAcmeOptions extends Omit<IAcmeOptions, 'routeForwards'> {
31
48
  }
32
49
  export interface IRustChallengeOptions extends Required<Pick<ISmartProxyChallengeOptions, 'cookieSigningKey' | 'pendingCookieName' | 'clearanceCookieName' | 'reservedPathPrefix' | 'relaySocketPath' | 'relayTimeoutMs' | 'pendingTtlSeconds'>> {
@@ -1,2 +1,3 @@
1
- export {};
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVzdC10eXBlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvbW9kZWxzL3J1c3QtdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
1
+ /** `errorCode` of a Rust `start` or `updateRoutes` response that refused its routes in validation. */
2
+ export const RUST_ROUTE_VALIDATION_ERROR_CODE = 'ROUTE_VALIDATION';
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVzdC10eXBlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvbW9kZWxzL3J1c3QtdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBOENBLHNHQUFzRztBQUN0RyxNQUFNLENBQUMsTUFBTSxnQ0FBZ0MsR0FBRyxrQkFBa0IsQ0FBQyJ9
@@ -1,13 +1,29 @@
1
1
  import * as plugins from '../../plugins.js';
2
- import type { IRustCertificateStatus, IRustChallengeOptions, IRustMetricsSnapshot, IRustProxyOptions, IRustMetricsHistory, IRustRouteConfig, IRustStatistics } from './models/rust-types.js';
2
+ import { type IRustCertificateStatus, type IRustChallengeOptions, type IRustMetricsSnapshot, type IRustProxyOptions, type IRustMetricsHistory, type IRustRouteConfig, type IRustStatistics } from './models/rust-types.js';
3
3
  import type { IActiveConnectionSnapshot, IActiveConnectionSnapshotOptions, IActiveConnectionSnapshotPage, ISmartProxySecurityPolicy } from './models/interfaces.js';
4
+ /**
5
+ * Turn a Rust `ROUTE_VALIDATION` refusal into a `RouteValidationError` carrying the
6
+ * per-route reasons from its `errorData`. Any other error is returned unchanged.
7
+ *
8
+ * `routeLabels` holds `RouteValidator.getRouteLabels()` of the caller's routes, one per sent
9
+ * route. Entries are relabelled by index: the preprocessor names unnamed routes for Rust, so
10
+ * the engine's own labels can differ from the ones the caller sees.
11
+ */
12
+ export declare function toRouteValidationError(error: unknown, routeLabels: string[]): unknown;
13
+ export interface IRustProxyBridgeOptions {
14
+ /**
15
+ * Explicit path to the RustProxy binary. When set, it is the only candidate:
16
+ * `spawn()` rejects with a `RustBinaryLocatorError` if it is missing or not executable.
17
+ */
18
+ binaryPath?: string;
19
+ }
4
20
  /**
5
21
  * Bridge between TypeScript SmartProxy and the Rust binary.
6
22
  * Wraps @push.rocks/smartrust's RustBridge with type-safe command definitions.
7
23
  */
8
24
  export declare class RustProxyBridge extends plugins.EventEmitter {
9
25
  private bridge;
10
- constructor();
26
+ constructor(options?: IRustProxyBridgeOptions);
11
27
  /**
12
28
  * Spawn the Rust binary in management mode.
13
29
  * Returns true if the binary was found and spawned successfully.
@@ -23,9 +39,17 @@ export declare class RustProxyBridge extends plugins.EventEmitter {
23
39
  * Whether the bridge is currently running.
24
40
  */
25
41
  get running(): boolean;
26
- startProxy(config: IRustProxyOptions): Promise<void>;
42
+ /**
43
+ * @param routeLabels `RouteValidator.getRouteLabels()` of the routes before preprocessing,
44
+ * used to label a route validation refusal.
45
+ */
46
+ startProxy(config: IRustProxyOptions, routeLabels: string[]): Promise<void>;
27
47
  stopProxy(): Promise<void>;
28
- updateRoutes(routes: IRustRouteConfig[]): Promise<void>;
48
+ /**
49
+ * @param routeLabels `RouteValidator.getRouteLabels()` of the routes before preprocessing,
50
+ * used to label a route validation refusal.
51
+ */
52
+ updateRoutes(routes: IRustRouteConfig[], routeLabels: string[]): Promise<void>;
29
53
  setSecurityPolicy(policy: ISmartProxySecurityPolicy): Promise<void>;
30
54
  getMetrics(): Promise<IRustMetricsSnapshot>;
31
55
  getMetricsHistory(seconds: number): Promise<IRustMetricsHistory>;
@@ -1,5 +1,7 @@
1
1
  import * as plugins from '../../plugins.js';
2
2
  import { logger } from '../../core/utils/logger.js';
3
+ import { RUST_ROUTE_VALIDATION_ERROR_CODE, } from './models/rust-types.js';
4
+ import { RouteValidationError } from './utils/route-validation-error.js';
3
5
  const MAXIMUM_RUST_CONNECTION_ID = 18446744073709551615n;
4
6
  const MAXIMUM_METRICS_HISTORY_SECONDS = 600;
5
7
  function validateActiveConnectionSnapshotOptions(options) {
@@ -13,6 +15,52 @@ function validateActiveConnectionSnapshotOptions(options) {
13
15
  throw new Error('Active connection snapshot afterId exceeds the Rust u64 range');
14
16
  }
15
17
  }
18
+ function isRustRouteValidationErrorData(data, sentRouteCount) {
19
+ if (typeof data !== 'object'
20
+ || data === null
21
+ || !('routes' in data)
22
+ || !Array.isArray(data.routes)
23
+ || data.routes.length === 0) {
24
+ return false;
25
+ }
26
+ return data.routes.every((issue) => typeof issue === 'object'
27
+ && issue !== null
28
+ && 'index' in issue
29
+ && typeof issue.index === 'number'
30
+ && Number.isSafeInteger(issue.index)
31
+ && issue.index >= 0
32
+ && issue.index < sentRouteCount
33
+ && 'route' in issue
34
+ && typeof issue.route === 'string'
35
+ && 'messages' in issue
36
+ && Array.isArray(issue.messages)
37
+ && issue.messages.every((message) => typeof message === 'string'));
38
+ }
39
+ /**
40
+ * Turn a Rust `ROUTE_VALIDATION` refusal into a `RouteValidationError` carrying the
41
+ * per-route reasons from its `errorData`. Any other error is returned unchanged.
42
+ *
43
+ * `routeLabels` holds `RouteValidator.getRouteLabels()` of the caller's routes, one per sent
44
+ * route. Entries are relabelled by index: the preprocessor names unnamed routes for Rust, so
45
+ * the engine's own labels can differ from the ones the caller sees.
46
+ */
47
+ export function toRouteValidationError(error, routeLabels) {
48
+ if (!(error instanceof plugins.smartrust.RustBridgeRequestError)
49
+ || error.responseErrorCode !== RUST_ROUTE_VALIDATION_ERROR_CODE) {
50
+ return error;
51
+ }
52
+ if (!isRustRouteValidationErrorData(error.responseErrorData, routeLabels.length)) {
53
+ return new Error(`RustProxy sent a route validation refusal with malformed errorData: ${error.message}`, {
54
+ cause: error,
55
+ });
56
+ }
57
+ return new RouteValidationError('Route validation failed', error.responseErrorData.routes.map(({ index, messages }) => ({ index, route: routeLabels[index], messages })));
58
+ }
59
+ function assertOneLabelPerRoute(routes, routeLabels) {
60
+ if (routes.length !== routeLabels.length) {
61
+ throw new Error(`RustProxyBridge got ${routeLabels.length} route label(s) for ${routes.length} route(s); they must match one to one`);
62
+ }
63
+ }
16
64
  /**
17
65
  * Get the package root directory using import.meta.url.
18
66
  * This file is at ts/proxies/smart-proxy/, so package root is 3 levels up.
@@ -59,10 +107,11 @@ function buildLocalPaths() {
59
107
  */
60
108
  export class RustProxyBridge extends plugins.EventEmitter {
61
109
  bridge;
62
- constructor() {
110
+ constructor(options = {}) {
63
111
  super();
64
112
  this.bridge = new plugins.smartrust.RustBridge({
65
113
  binaryName: 'rustproxy',
114
+ binaryPath: options.binaryPath,
66
115
  envVarName: 'SMARTPROXY_RUST_BINARY',
67
116
  platformPackagePrefix: '@push.rocks/smartproxy',
68
117
  localPaths: buildLocalPaths(),
@@ -102,14 +151,34 @@ export class RustProxyBridge extends plugins.EventEmitter {
102
151
  return this.bridge.running;
103
152
  }
104
153
  // --- Convenience methods for each management command ---
105
- async startProxy(config) {
106
- await this.bridge.sendCommand('start', { config });
154
+ /**
155
+ * @param routeLabels `RouteValidator.getRouteLabels()` of the routes before preprocessing,
156
+ * used to label a route validation refusal.
157
+ */
158
+ async startProxy(config, routeLabels) {
159
+ assertOneLabelPerRoute(config.routes, routeLabels);
160
+ try {
161
+ await this.bridge.sendCommand('start', { config });
162
+ }
163
+ catch (error) {
164
+ throw toRouteValidationError(error, routeLabels);
165
+ }
107
166
  }
108
167
  async stopProxy() {
109
168
  await this.bridge.sendCommand('stop', {});
110
169
  }
111
- async updateRoutes(routes) {
112
- await this.bridge.sendCommand('updateRoutes', { routes });
170
+ /**
171
+ * @param routeLabels `RouteValidator.getRouteLabels()` of the routes before preprocessing,
172
+ * used to label a route validation refusal.
173
+ */
174
+ async updateRoutes(routes, routeLabels) {
175
+ assertOneLabelPerRoute(routes, routeLabels);
176
+ try {
177
+ await this.bridge.sendCommand('updateRoutes', { routes });
178
+ }
179
+ catch (error) {
180
+ throw toRouteValidationError(error, routeLabels);
181
+ }
113
182
  }
114
183
  async setSecurityPolicy(policy) {
115
184
  await this.bridge.sendCommand('setSecurityPolicy', { policy });
@@ -167,4 +236,4 @@ export class RustProxyBridge extends plugins.EventEmitter {
167
236
  await this.bridge.sendCommand('setDatagramHandlerRelay', { socketPath });
168
237
  }
169
238
  }
170
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVzdC1wcm94eS1icmlkZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L3J1c3QtcHJveHktYnJpZGdlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBWXBELE1BQU0sMEJBQTBCLEdBQUcscUJBQTJCLENBQUM7QUFDL0QsTUFBTSwrQkFBK0IsR0FBRyxHQUFHLENBQUM7QUFFNUMsU0FBUyx1Q0FBdUMsQ0FBQyxPQUF5QztJQUN4RixJQUFJLE9BQU8sQ0FBQyxPQUFPLEtBQUssU0FBUyxFQUFFLENBQUM7UUFDbEMsT0FBTztJQUNULENBQUM7SUFDRCxJQUFJLE9BQU8sT0FBTyxDQUFDLE9BQU8sS0FBSyxRQUFRLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQzFFLE1BQU0sSUFBSSxLQUFLLENBQUMsdUVBQXVFLENBQUMsQ0FBQztJQUMzRixDQUFDO0lBQ0QsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLDBCQUEwQixFQUFFLENBQUM7UUFDekQsTUFBTSxJQUFJLEtBQUssQ0FBQywrREFBK0QsQ0FBQyxDQUFDO0lBQ25GLENBQUM7QUFDSCxDQUFDO0FBMkJEOzs7R0FHRztBQUNILFNBQVMsY0FBYztJQUNyQixNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDakYsT0FBTyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLENB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
239
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVzdC1wcm94eS1icmlkZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L3J1c3QtcHJveHktYnJpZGdlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3BELE9BQU8sRUFDTCxnQ0FBZ0MsR0FTakMsTUFBTSx3QkFBd0IsQ0FBQztBQUNoQyxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUd6RSxNQUFNLDBCQUEwQixHQUFHLHFCQUEyQixDQUFDO0FBQy9ELE1BQU0sK0JBQStCLEdBQUcsR0FBRyxDQUFDO0FBRTVDLFNBQVMsdUNBQXVDLENBQUMsT0FBeUM7SUFDeEYsSUFBSSxPQUFPLENBQUMsT0FBTyxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQ2xDLE9BQU87SUFDVCxDQUFDO0lBQ0QsSUFBSSxPQUFPLE9BQU8sQ0FBQyxPQUFPLEtBQUssUUFBUSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUMxRSxNQUFNLElBQUksS0FBSyxDQUFDLHVFQUF1RSxDQUFDLENBQUM7SUFDM0YsQ0FBQztJQUNELElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsR0FBRywwQkFBMEIsRUFBRSxDQUFDO1FBQ3pELE1BQU0sSUFBSSxLQUFLLENBQUMsK0RBQStELENBQUMsQ0FBQztJQUNuRixDQUFDO0FBQ0gsQ0FBQztBQUVELFNBQVMsOEJBQThCLENBQ3JDLElBQWEsRUFDYixjQUFzQjtJQUV0QixJQUNFLE9BQU8sSUFBSSxLQUFLLFFBQVE7V0FDckIsSUFBSSxLQUFLLElBQUk7V0FDYixDQUFDLENBQUMsUUFBUSxJQUFJLElBQUksQ0FBQztXQUNuQixDQUFD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