/** * ID Normalization Utilities * FE-TYPE-001: Ensure all IDs are string (UUID) not number * * Provides utilities to normalize IDs to strings (UUIDs) consistently * across the application, preventing type mismatches. */ /** * Normalize an ID to a string * Handles both string and number IDs, converting them to strings * * @param id - ID value (string, number, or undefined/null) * @returns Normalized string ID or undefined if input is null/undefined * * @example * ```typescript * normalizeId(123) // "123" * normalizeId("abc-123") // "abc-123" * normalizeId(null) // undefined * ``` */ export function normalizeId( id: string | number | null | undefined, ): string | undefined { if (id === null || id === undefined) { return undefined; } if (typeof id === 'string') { return id; } if (typeof id === 'number') { return String(id); } // Fallback for any other type return String(id); } /** * Normalize an ID to a string (required) * Throws an error if ID is null or undefined * * @param id - ID value (string, number, or undefined/null) * @returns Normalized string ID * @throws Error if id is null or undefined * * @example * ```typescript * normalizeIdRequired(123) // "123" * normalizeIdRequired("abc-123") // "abc-123" * normalizeIdRequired(null) // throws Error * ``` */ export function normalizeIdRequired( id: string | number | null | undefined, ): string { const normalized = normalizeId(id); if (normalized === undefined) { throw new Error('ID is required but was null or undefined'); } return normalized; } /** * Normalize an array of IDs to strings * * @param ids - Array of ID values * @returns Array of normalized string IDs * * @example * ```typescript * normalizeIds([1, 2, "abc"]) // ["1", "2", "abc"] * ``` */ export function normalizeIds( ids: (string | number | null | undefined)[], ): string[] { return ids .map((id) => normalizeId(id)) .filter((id): id is string => id !== undefined); } /** * Normalize IDs in an object * Recursively normalizes all fields that match common ID field names * * @param obj - Object to normalize * @param idFields - Optional array of field names to normalize (default: common ID fields) * @returns New object with normalized IDs * * @example * ```typescript * normalizeObjectIds({ id: 123, user_id: 456 }) * // { id: "123", user_id: "456" } * ``` */ export function normalizeObjectIds>( obj: T, idFields: string[] = [ 'id', 'user_id', 'track_id', 'playlist_id', 'conversation_id', 'message_id', 'sender_id', 'creator_id', 'created_by', 'parent_id', 'parent_message_id', ], ): T { if (!obj || typeof obj !== 'object') { return obj; } const normalized = { ...obj } as Record; for (const [key, value] of Object.entries(normalized)) { // Normalize ID fields if (idFields.includes(key)) { normalized[key] = normalizeId(value); } // Recursively normalize nested objects else if ( value && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date) ) { normalized[key] = normalizeObjectIds(value, idFields); } // Normalize arrays of objects else if ( Array.isArray(value) && value.length > 0 && typeof value[0] === 'object' ) { normalized[key] = value.map((item) => typeof item === 'object' && item !== null ? normalizeObjectIds(item, idFields) : item, ); } } return normalized as T; } /** * Normalize IDs in an array of objects * * @param items - Array of objects to normalize * @param idFields - Optional array of field names to normalize * @returns Array of objects with normalized IDs * * @example * ```typescript * normalizeArrayIds([{ id: 1 }, { id: 2 }]) * // [{ id: "1" }, { id: "2" }] * ``` */ export function normalizeArrayIds>( items: T[], idFields?: string[], ): T[] { return items.map((item) => normalizeObjectIds(item, idFields)); } /** * Type guard to check if a value is a valid ID (string or number) */ export function isValidId(id: unknown): id is string | number { return typeof id === 'string' || typeof id === 'number'; } /** * Type guard to check if a value is a valid string ID */ export function isValidStringId(id: unknown): id is string { return typeof id === 'string' && id.length > 0; }