export enum MessageType { ACK = 0, TEXT = 1, ID_RESPONSE = 3, TYPING = 4, CURRENTLY_TYPING = 5, CONNECTED_USERS = 6, DESIRED_NAME = 7, } export type ServerMessage = { __ctx?: string; type: MessageType; date: number; }; export function isServerMessage( obj: unknown, type?: MessageType ): obj is ServerMessage { if (typeof obj !== "object") return false; if (obj === null) return false; if ( !Object.hasOwnProperty.call(obj, "type") || typeof (obj as { type: unknown }).type !== "number" || (type === undefined && !Object.hasOwnProperty.call( MessageType, (obj as { type: number }).type )) || (type !== undefined && (obj as { type: number }).type !== type) ) { return false; } if ( !Object.hasOwnProperty.call(obj, "date") || typeof (obj as { date: unknown }).date !== "number" ) { return false; } if ( Object.hasOwnProperty.call(obj, "__ctx") && typeof (obj as { __ctx: unknown }).__ctx !== "string" && typeof (obj as { __ctx: unknown }).__ctx !== "undefined" ) { return false; } return true; } export type TextMessage = ServerMessage & { type: MessageType.TEXT; author: string; content: string; }; export function isTextMessage(obj: unknown): obj is TextMessage { if (!isServerMessage(obj, MessageType.TEXT)) return false; if ( !Object.hasOwnProperty.call(obj, "author") || typeof (obj as ServerMessage & { author: unknown }).author !== "string" ) { return false; } if ( !Object.hasOwnProperty.call(obj, "author") || typeof (obj as ServerMessage & { content: unknown }).content !== "string" ) { return false; } return true; } export type AckMessage = ServerMessage & { type: MessageType.ACK; }; export function isAckMessage(obj: unknown): obj is AckMessage { return isServerMessage(obj, MessageType.ACK); } export type IdResponseMessage = ServerMessage & { type: MessageType.ID_RESPONSE; authorId: string; }; export function isIdResponseMessage(obj: unknown): obj is IdResponseMessage { if (!isServerMessage(obj, MessageType.ID_RESPONSE)) return false; if ( !Object.hasOwnProperty.call(obj, "authorId") || typeof (obj as ServerMessage & { authorId: unknown }).authorId !== "string" ) { return false; } return true; } export type TypingMessage = ServerMessage & { type: MessageType.TYPING; }; export function isTypingMessage(obj: unknown): obj is TypingMessage { return isServerMessage(obj, MessageType.TYPING); } export type CurrentlyTypingMessage = ServerMessage & { type: MessageType.CURRENTLY_TYPING; currently: string[]; }; export function isCurrentlyTypingMessage( obj: unknown ): obj is CurrentlyTypingMessage { if (!isServerMessage(obj, MessageType.CURRENTLY_TYPING)) { return false; } if ( !Object.hasOwnProperty.call(obj, "currently") || typeof obj !== "object" || !Array.isArray((obj as ServerMessage & { currently: unknown }).currently) || !Array.prototype.every.call( (obj as ServerMessage & { currently: unknown[] }).currently, (element) => typeof element === "string" ) ) { return false; } return true; } export type ConnectedUser = { id: string; desiredName?: string; }; export function isConnectedUser(obj: unknown): obj is ConnectedUser { if (typeof obj !== "object" || obj === null) { return false; } if ( !Object.prototype.hasOwnProperty.call(obj, "id") || typeof (obj as { id: unknown }).id !== "string" ) { return false; } if ( Object.prototype.hasOwnProperty.call(obj, "desiredName") && typeof (obj as { desiredName: unknown }).desiredName !== "string" ) { return false; } return true; } export type ConnectedUsersMessage = ServerMessage & { type: MessageType.CONNECTED_USERS; connected: ConnectedUser[]; }; export function isConnectedUsersMessage( obj: unknown ): obj is ConnectedUsersMessage { if (!isServerMessage(obj, MessageType.CONNECTED_USERS)) { return false; } if ( typeof (obj as ServerMessage & { connected: unknown }).connected !== "object" || !Array.isArray((obj as ServerMessage & { connected: unknown }).connected) ) { return false; } if ( !Array.prototype.every.call( (obj as ServerMessage & { connected: unknown[] }).connected, (maybeUser) => isConnectedUser(maybeUser) ) ) { return false; } return true; } export type DesiredNameMessage = ServerMessage & { type: MessageType.DESIRED_NAME; desiredName: string; }; export function isDesiredNameMessage(obj: unknown): obj is DesiredNameMessage { if (!isServerMessage(obj, MessageType.DESIRED_NAME)) { return false; } if ( Object.hasOwnProperty.call(obj, "desiredName") && typeof (obj as ServerMessage & { desiredName: unknown }).desiredName !== "string" ) { return false; } return true; }