/** * Safe JSON Parsing Utilities * * Provides try-catch protected JSON parsing with optional default values * and context-aware error messages. * * Usage: * - safeJsonParse: Returns result object with success/failure status * - parseJsonOrDefault: Returns parsed value or default on failure * - parseJsonOrThrow: Returns parsed value or throws friendly error */ export interface SafeJsonParseResult { success: boolean; data?: T; error?: string; } /** * Safely parse a JSON string with error handling * * @param text - The JSON string to parse * @param defaultValue - Optional default value to return on parse failure * @returns Result object with success status, data, and optional error message * * @example * const result = safeJsonParse(jsonString); * if (result.success) { * console.log(result.data); * } else { * console.error(result.error); * } */ export function safeJsonParse(text: string, defaultValue?: T): SafeJsonParseResult { try { const data = JSON.parse(text) as T; return { success: true, data }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown JSON parse error'; // Log truncated input for debugging const truncatedInput = text.length > 100 ? `${text.substring(0, 100)}...` : text; console.warn('[json-utils] Parse failed:', errorMessage, 'Input:', truncatedInput); return { success: false, error: errorMessage, data: defaultValue, }; } } /** * Safely parse JSON and return default value on failure * * Use this when you have a sensible default and don't need to know * about parse failures. * * @param text - The JSON string to parse * @param defaultValue - The value to return if parsing fails * @returns The parsed data or the default value * * @example * const config = parseJsonOrDefault(rawConfig, defaultConfig); */ export function parseJsonOrDefault(text: string, defaultValue: T): T { const result = safeJsonParse(text, defaultValue); return result.data!; } /** * Safely parse JSON or throw a friendly error with context * * Use this when JSON parsing is required and failures should halt execution * with a clear error message. * * @param text - The JSON string to parse * @param context - Optional context for the error message (e.g., "loading config") * @returns The parsed data * @throws Error with context-aware message if parsing fails * * @example * try { * const data = parseJsonOrThrow(rawJson, 'parsing user config'); * } catch (error) { * showToast(error.message); // "JSON parse failed (parsing user config): Unexpected token..." * } */ export function parseJsonOrThrow(text: string, context?: string): T { const result = safeJsonParse(text); if (!result.success) { throw new Error(`JSON parse failed${context ? ` (${context})` : ''}: ${result.error}`); } return result.data!; } /** * Type guard to check if a value is a valid JSON-compatible object * * @param value - The value to check * @returns True if the value can be safely serialized to JSON */ export function isJsonSerializable(value: unknown): boolean { try { JSON.stringify(value); return true; } catch (_e) { return false; } } /** * Safely stringify a value to JSON * * @param value - The value to stringify * @param fallback - Fallback string if stringification fails * @returns JSON string or fallback */ export function safeJsonStringify(value: unknown, fallback = '{}'): string { try { return JSON.stringify(value); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown stringify error'; console.warn('[json-utils] Stringify failed:', errorMessage); return fallback; } } /** * Safely stringify with pretty formatting * * @param value - The value to stringify * @param indent - Number of spaces for indentation (default: 2) * @param fallback - Fallback string if stringification fails * @returns Formatted JSON string or fallback */ export function safeJsonStringifyPretty(value: unknown, indent = 2, fallback = '{}'): string { try { return JSON.stringify(value, null, indent); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown stringify error'; console.warn('[json-utils] Pretty stringify failed:', errorMessage); return fallback; } } /** * Deep clone an object using JSON serialization * * Note: This only works for JSON-serializable data (no functions, undefined, symbols, etc.) * * @param value - The value to clone * @returns A deep clone of the value * @throws Error if the value cannot be serialized */ export function deepClone(value: T): T { return JSON.parse(JSON.stringify(value)) as T; } /** * Safely deep clone an object with fallback * * @param value - The value to clone * @param fallback - Fallback value if cloning fails * @returns A deep clone of the value or the fallback */ export function safeDeepClone(value: T, fallback: T): T { try { return JSON.parse(JSON.stringify(value)) as T; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown clone error'; console.warn('[json-utils] Deep clone failed:', errorMessage); return fallback; } }