Files
Wand-Enhancer-luanslimadev-1/web-panel/bridge/src/protocol-router.ts
T
kitbyte a0b3968d33 refactor(web-panel): update architecture + cheat i18n
Reorganize the panel around product capabilities and integrate the
remote i18n feature from origin/master into the new structure.

- Restructure src into capability features (app, trainer, library,
  remote-session, appearance, shared); move the bridge to bridge/src.
- Add lingui-based UI localization and wrap remaining user-facing
  strings (Trans / msg macros); fix the 'END В·' mojibake.
- Port WeMod cheat-metadata i18n: capture the access token from the
  snapshot's renderer in the bridge and fetch localized trainer_meta
  in remote-session.i18n; guarantee snapshot sync on token failure.
- Wire vitest to the app's lingui/preact pipeline (mergeConfig).
2026-06-15 00:10:49 +03:00

79 lines
2.9 KiB
TypeScript

import webContract from '../../protocol/web-contract.json';
const BRIDGE_PROTOCOL_VERSION = webContract.protocolVersion;
export function validateClientMessage(message, handshaken) {
if (!isRecord(message) || typeof message.type !== 'string' || !isRecord(message.payload)) {
return invalid('invalid_message', 'Expected a protocol envelope with an object payload.');
}
if (message.version !== BRIDGE_PROTOCOL_VERSION) {
return invalid('protocol_mismatch', `Unsupported protocol version ${String(message.version)}.`);
}
if (message.requestId !== null && typeof message.requestId !== 'string') {
return invalid('invalid_request_id', 'requestId must be a string or null.');
}
if (message.type === 'hello') {
if (message.payload.client !== 'mobile-web' || typeof message.payload.clientVersion !== 'string' || !isRecord(message.payload.capabilities)) {
return invalid('invalid_hello', 'The hello payload is incomplete.');
}
return { ok: true };
}
if (!handshaken) {
return invalid('handshake_required', 'Send a compatible hello message before commands.');
}
if (message.type === 'set_value') {
if (!safeString(message.payload.trainerId) || !safeString(message.payload.target) || !('value' in message.payload)) {
return invalid('invalid_set_value', 'trainerId, target and value are required.');
}
return { ok: true };
}
if (message.type === 'remote_command') {
if (message.payload.action !== 'launch' && message.payload.action !== 'stop') {
return invalid('invalid_command', 'Unknown remote command.');
}
return { ok: true };
}
return invalid('unknown_message', 'Unknown protocol message type.');
}
export function validateSetValueTarget(message, snapshot) {
const target = safeString(message.payload?.target);
const requestedTrainerId = safeString(message.payload?.trainerId);
const activeTrainerId = snapshot?.trainerMeta?.trainer?.trainerId || '';
if (!snapshot || requestedTrainerId !== activeTrainerId) {
return invalid('trainer_mismatch', 'The requested trainer is not active.');
}
const cheat = snapshot.trainerMeta.schema.cheats.find((entry) => entry.target === target);
if (!target || !cheat || !(target in snapshot.trainerValues.values)) {
return invalid('invalid_target', 'Unknown cheat target.');
}
return {
ok: true,
trainerId: activeTrainerId,
target,
cheat,
value: cheat.type === 'toggle' ? Boolean(message.payload.value) : message.payload.value,
};
}
function invalid(code, message) {
return { ok: false, error: { code, message } };
}
function isRecord(value) {
return typeof value === 'object' && value !== null;
}
function safeString(value) {
return typeof value === 'string' && value.length > 0 ? value : '';
}