123 lines
3.8 KiB
TypeScript
123 lines
3.8 KiB
TypeScript
|
|
import type { IRouteConfig, IRouteAction, IRouteTarget } from './models/route-types.js';
|
||
|
|
import { logger } from '../../core/utils/logger.js';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Preprocesses routes before sending them to Rust.
|
||
|
|
*
|
||
|
|
* Strips non-serializable fields (functions, callbacks) and classifies
|
||
|
|
* routes that must be handled by TypeScript (socket-handler, dynamic host/port).
|
||
|
|
*/
|
||
|
|
export class RoutePreprocessor {
|
||
|
|
/**
|
||
|
|
* Map of route name/id → original route config (with JS functions preserved).
|
||
|
|
* Used by the socket handler server to look up the original handler.
|
||
|
|
*/
|
||
|
|
private originalRoutes = new Map<string, IRouteConfig>();
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Preprocess routes for the Rust binary.
|
||
|
|
*
|
||
|
|
* - Routes with `socketHandler` callbacks are marked as socket-handler type
|
||
|
|
* (Rust will relay these back to TS)
|
||
|
|
* - Routes with dynamic `host`/`port` functions are converted to socket-handler
|
||
|
|
* type (Rust relays, TS resolves the function)
|
||
|
|
* - Non-serializable fields are stripped
|
||
|
|
* - Original routes are preserved in the local map for handler lookup
|
||
|
|
*/
|
||
|
|
public preprocessForRust(routes: IRouteConfig[]): IRouteConfig[] {
|
||
|
|
this.originalRoutes.clear();
|
||
|
|
return routes.map((route, index) => this.preprocessRoute(route, index));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get the original route config (with JS functions) by route name or id.
|
||
|
|
*/
|
||
|
|
public getOriginalRoute(routeKey: string): IRouteConfig | undefined {
|
||
|
|
return this.originalRoutes.get(routeKey);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get all original routes that have socket handlers or dynamic functions.
|
||
|
|
*/
|
||
|
|
public getHandlerRoutes(): Map<string, IRouteConfig> {
|
||
|
|
return new Map(this.originalRoutes);
|
||
|
|
}
|
||
|
|
|
||
|
|
private preprocessRoute(route: IRouteConfig, index: number): IRouteConfig {
|
||
|
|
const routeKey = route.name || route.id || `route_${index}`;
|
||
|
|
|
||
|
|
// Check if this route needs TS-side handling
|
||
|
|
const needsTsHandling = this.routeNeedsTsHandling(route);
|
||
|
|
|
||
|
|
if (needsTsHandling) {
|
||
|
|
// Store the original route for handler lookup
|
||
|
|
this.originalRoutes.set(routeKey, route);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Create a clean copy for Rust
|
||
|
|
const cleanRoute: IRouteConfig = {
|
||
|
|
...route,
|
||
|
|
action: this.cleanAction(route.action, routeKey, needsTsHandling),
|
||
|
|
};
|
||
|
|
|
||
|
|
// Ensure we have a name for handler lookup
|
||
|
|
if (!cleanRoute.name && !cleanRoute.id) {
|
||
|
|
cleanRoute.name = routeKey;
|
||
|
|
}
|
||
|
|
|
||
|
|
return cleanRoute;
|
||
|
|
}
|
||
|
|
|
||
|
|
private routeNeedsTsHandling(route: IRouteConfig): boolean {
|
||
|
|
// Socket handler routes always need TS
|
||
|
|
if (route.action.type === 'socket-handler' && route.action.socketHandler) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Routes with dynamic host/port functions need TS
|
||
|
|
if (route.action.targets) {
|
||
|
|
for (const target of route.action.targets) {
|
||
|
|
if (typeof target.host === 'function' || typeof target.port === 'function') {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
private cleanAction(action: IRouteAction, routeKey: string, needsTsHandling: boolean): IRouteAction {
|
||
|
|
const cleanAction: IRouteAction = { ...action };
|
||
|
|
|
||
|
|
if (needsTsHandling) {
|
||
|
|
// Convert to socket-handler type for Rust (Rust will relay back to TS)
|
||
|
|
cleanAction.type = 'socket-handler';
|
||
|
|
// Remove the JS handler (not serializable)
|
||
|
|
delete (cleanAction as any).socketHandler;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Clean targets - replace functions with static values
|
||
|
|
if (cleanAction.targets) {
|
||
|
|
cleanAction.targets = cleanAction.targets.map(t => this.cleanTarget(t));
|
||
|
|
}
|
||
|
|
|
||
|
|
return cleanAction;
|
||
|
|
}
|
||
|
|
|
||
|
|
private cleanTarget(target: IRouteTarget): IRouteTarget {
|
||
|
|
const clean: IRouteTarget = { ...target };
|
||
|
|
|
||
|
|
// Replace function host with placeholder
|
||
|
|
if (typeof clean.host === 'function') {
|
||
|
|
clean.host = 'localhost';
|
||
|
|
}
|
||
|
|
|
||
|
|
// Replace function port with placeholder
|
||
|
|
if (typeof clean.port === 'function') {
|
||
|
|
clean.port = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return clean;
|
||
|
|
}
|
||
|
|
}
|