/** * JSON Parsing Utilities * * Shared utilities for safely parsing JSON values that may be strings or already parsed. */ /** * Parses a value that might be a JSON string or already an object. * Returns a typed object with fallback support. * * @param value - The value to parse (string, object, or undefined) * @param fallback - Fallback value if parsing fails (defaults to empty object) * @returns The parsed object or fallback * * @example * const schema = parseJsonObject(page.ui_schema_json, {}); * const settings = parseJsonObject(settingsValue, { enabled: false }); */ export const parseJsonObject = (value?: unknown, fallback?: T): T => { if (!value) return (fallback || ({} as T)) as T; try { if (typeof value === 'string') { const parsed = JSON.parse(value); return (parsed || fallback || {}) as T; } if (typeof value === 'object') { return value as T; } return (fallback || ({} as T)) as T; } catch { return (fallback || ({} as T)) as T; } }; /** * Parses a JSON field that might be a string, object, or null. * More lenient than parseJsonObject - returns the original value on parse error. * * @param value - The value to parse * @returns The parsed value, original value, or null * * @example * const content = parseJsonField(element.content_json); * const style = parseJsonField(element.style_json); */ export const parseJsonField = (value: unknown): unknown => { if (value === null || value === undefined) return null; if (typeof value !== 'string') return value; if (!value.trim()) return null; try { return JSON.parse(value); } catch { return value; } }; /** * Extracts preview text from parsed content by checking common text field names. * * @param content - Parsed content object * @returns The first text value found, or null * * @example * const previewText = getElementPreviewText(parseJsonField(element.content_json)); */ export const getElementPreviewText = (content: unknown): string | null => { if (typeof content === 'string') return content; if (!content || typeof content !== 'object') return null; const candidateKeys = [ 'title', 'text', 'subtitle', 'description', 'body', 'label', 'value', ]; const contentRecord = content as Record; const firstTextKey = candidateKeys.find( (key) => typeof contentRecord[key] === 'string', ); return firstTextKey ? String(contentRecord[firstTextKey]) : null; };