65 lines
1.8 KiB
TypeScript
65 lines
1.8 KiB
TypeScript
// message types between daemon and adapters
|
|
|
|
export type ClientMessage =
|
|
| { type: "join"; room: string }
|
|
| { type: "leave" }
|
|
| { type: "update"; data: number[] } // yjs update as byte array
|
|
| { type: "awareness"; data: number[] };
|
|
|
|
export type AwarenessState = {
|
|
clientId: number;
|
|
cursor?: { line: number; col: number };
|
|
selection?: { startLine: number; startCol: number; endLine: number; endCol: number };
|
|
name?: string;
|
|
};
|
|
|
|
export type ServerMessage =
|
|
| { type: "sync"; data: number[] } // full yjs state
|
|
| { type: "update"; data: number[] }
|
|
| { type: "awareness"; data: AwarenessState }
|
|
| { type: "peers"; count: number }
|
|
| { type: "error"; message: string };
|
|
|
|
export type BridgeMessage =
|
|
| { type: "ready" }
|
|
| { type: "connected"; room: string }
|
|
| { type: "disconnected" }
|
|
| { type: "content"; text: string }
|
|
| { type: "peers"; count: number }
|
|
| { type: "error"; message: string };
|
|
|
|
export function encode(msg: ServerMessage): string {
|
|
return JSON.stringify(msg);
|
|
}
|
|
|
|
function isClientMessage(obj: unknown): obj is ClientMessage {
|
|
if (typeof obj !== "object" || obj === null) return false;
|
|
const msg = obj as Record<string, unknown>;
|
|
|
|
switch (msg.type) {
|
|
case "join":
|
|
return typeof msg.room === "string";
|
|
case "leave":
|
|
return true;
|
|
case "update":
|
|
return (
|
|
Array.isArray(msg.data) && msg.data.every((n) => typeof n === "number")
|
|
);
|
|
case "awareness":
|
|
return (
|
|
Array.isArray(msg.data) && msg.data.every((n) => typeof n === "number")
|
|
);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function decode(raw: string): ClientMessage | null {
|
|
try {
|
|
const parsed = JSON.parse(raw);
|
|
if (!isClientMessage(parsed)) return null;
|
|
return parsed;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|