Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | 151x 151x 151x 50x 151x 151x 151x 151x 151x 2x 2x 2x 149x 149x 1x 1x 148x 148x 93x 93x 2x 2x 19x 19x 9x 9x 2x 2x 3x 3x 20x 20x | import type { RawData, WebSocket } from "ws";
import type { Logger } from "@shared/logger";
import type { ClientToServerWSMessage } from "@shared/schema";
import { decodeClientMessage } from "../../services/ws-message-decoder";
import type { ClientState } from "./ws-session-manager";
function rawDataToString(data: RawData): string {
Iif (Array.isArray(data)) return Buffer.concat(data).toString();
Iif (data instanceof ArrayBuffer) return Buffer.from(data).toString();
return data.toString();
}
type MessageHandler<T extends ClientToServerWSMessage> = (
ws: WebSocket,
data: T,
clientState: ClientState,
) => void | Promise<void>;
export interface WsMessageRouterHandlers {
startSimulation: MessageHandler<Extract<ClientToServerWSMessage, { type: "start_simulation" }>>;
codeChanged: MessageHandler<Extract<ClientToServerWSMessage, { type: "code_changed" }>>;
stopSimulation: MessageHandler<Extract<ClientToServerWSMessage, { type: "stop_simulation" }>>;
pauseSimulation: MessageHandler<Extract<ClientToServerWSMessage, { type: "pause_simulation" }>>;
resumeSimulation: MessageHandler<Extract<ClientToServerWSMessage, { type: "resume_simulation" }>>;
serialInput: MessageHandler<Extract<ClientToServerWSMessage, { type: "serial_input" }>>;
setPinValue: MessageHandler<Extract<ClientToServerWSMessage, { type: "set_pin_value" }>>;
}
interface WsMessageRouterParams {
logger: Logger;
getClientState: (ws: WebSocket) => ClientState | undefined;
handlers: WsMessageRouterHandlers;
}
export class WsMessageRouter {
constructor(private readonly params: WsMessageRouterParams) {}
async route(ws: WebSocket, message: RawData): Promise<void> {
try {
const msgText = rawDataToString(message);
const data = decodeClientMessage(msgText);
// Type and size only: messages carry sketch source and serial input.
this.params.logger.debug(`[WS-IN] ${data?.type ?? "invalid"} (${msgText.length} chars)`);
if (!data) {
this.params.logger.warn("[WS] Rejected invalid client message");
ws.close(1008, "Invalid message");
return;
}
const clientState = this.params.getClientState(ws);
if (!clientState) {
this.params.logger.warn(
`[WS] Message received but clientState not found for type: ${data.type}`,
);
return;
}
await this.dispatch(ws, data, clientState);
} catch (error) {
this.params.logger.error(
`Invalid WebSocket message: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
private async dispatch(
ws: WebSocket,
data: ClientToServerWSMessage,
clientState: ClientState,
): Promise<void> {
switch (data.type) {
case "start_simulation":
await this.params.handlers.startSimulation(ws, data, clientState);
break;
case "code_changed":
await this.params.handlers.codeChanged(ws, data, clientState);
break;
case "stop_simulation":
await this.params.handlers.stopSimulation(ws, data, clientState);
break;
case "pause_simulation":
await this.params.handlers.pauseSimulation(ws, data, clientState);
break;
case "resume_simulation":
await this.params.handlers.resumeSimulation(ws, data, clientState);
break;
case "serial_input":
await this.params.handlers.serialInput(ws, data, clientState);
break;
case "set_pin_value":
await this.params.handlers.setPinValue(ws, data, clientState);
break;
}
}
}
|