import {MiniMessageClickEvent, MiniMessageComponent, MiniMessageHoverEvent} from './mini-message.types'; /** * What actually arrives over the wire can be looser than the strict MiniMessageComponent * type: some serializers emit bare strings inside `extra` instead of {text: "..."}, and * some use snake_case event keys (hover_event/click_event) with different subfield names * (e.g. click_event.command instead of clickEvent.value). This function tolerates both * and always returns a clean, strictly-typed tree. * * Call this once, right after JSON.parse, before the data enters your app's state: * * messageJson: normalizeComponent(JSON.parse(m.messageJson)) */ export function normalizeComponent(input: unknown): MiniMessageComponent { if (typeof input === 'string') { return {text: input}; } if (input === null || typeof input !== 'object') { return {text: ''}; } const raw = input as Record; const node: MiniMessageComponent = {...(raw as MiniMessageComponent)}; node.clickEvent = normalizeClickEvent(raw['clickEvent'] ?? raw['click_event']); node.hoverEvent = normalizeHoverEvent(raw['hoverEvent'] ?? raw['hover_event']); delete node['click_event' as keyof MiniMessageComponent]; delete node['hover_event' as keyof MiniMessageComponent]; if (Array.isArray(raw['extra'])) { node.extra = raw['extra'].map((child: unknown) => normalizeComponent(child)); } // auditShape(node); return node; } function normalizeClickEvent(raw: Record | undefined): MiniMessageClickEvent | undefined { if (!raw) return undefined; const value = raw['value'] ?? raw['command'] ?? raw['url'] ?? raw['page'] ?? ''; return {action: raw['action'], value: String(value)}; } function normalizeHoverEvent(raw: Record | undefined): MiniMessageHoverEvent | undefined { if (!raw) return undefined; const source = raw['value'] ?? raw['contents']; return { action: raw['action'], value: typeof source === 'string' ? source : source ? normalizeComponent(source) : undefined, }; } export function auditShape(node: unknown, path = 'root'): void { if (typeof node === 'string') { console.warn(`Bare string in tree at ${path}:`, node); return; } const n = node as Record; if (n['hover_event'] || n['click_event']) { console.warn(`snake_case event key at ${path}`, n); } (n['extra'] as unknown[] | undefined)?.forEach((c, i) => auditShape(c, `${path}.extra[${i}]`)); }