2025-12-25 13:27:28 +00:00
|
|
|
/**
|
|
|
|
|
* ID Normalization Utilities
|
|
|
|
|
* FE-TYPE-001: Ensure all IDs are string (UUID) not number
|
2026-01-13 18:47:57 +00:00
|
|
|
*
|
2025-12-25 13:27:28 +00:00
|
|
|
* 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
|
2026-01-13 18:47:57 +00:00
|
|
|
*
|
2025-12-25 13:27:28 +00:00
|
|
|
* @param id - ID value (string, number, or undefined/null)
|
|
|
|
|
* @returns Normalized string ID or undefined if input is null/undefined
|
2026-01-13 18:47:57 +00:00
|
|
|
*
|
2025-12-25 13:27:28 +00:00
|
|
|
* @example
|
|
|
|
|
* ```typescript
|
|
|
|
|
* normalizeId(123) // "123"
|
|
|
|
|
* normalizeId("abc-123") // "abc-123"
|
|
|
|
|
* normalizeId(null) // undefined
|
|
|
|
|
* ```
|
|
|
|
|
*/
|
2026-01-13 18:47:57 +00:00
|
|
|
export function normalizeId(
|
|
|
|
|
id: string | number | null | undefined,
|
|
|
|
|
): string | undefined {
|
2025-12-25 13:27:28 +00:00
|
|
|
if (id === null || id === undefined) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2025-12-25 13:27:28 +00:00
|
|
|
if (typeof id === 'string') {
|
|
|
|
|
return id;
|
|
|
|
|
}
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2025-12-25 13:27:28 +00:00
|
|
|
if (typeof id === 'number') {
|
|
|
|
|
return String(id);
|
|
|
|
|
}
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2025-12-25 13:27:28 +00:00
|
|
|
// Fallback for any other type
|
|
|
|
|
return String(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Normalize an ID to a string (required)
|
|
|
|
|
* Throws an error if ID is null or undefined
|
2026-01-13 18:47:57 +00:00
|
|
|
*
|
2025-12-25 13:27:28 +00:00
|
|
|
* @param id - ID value (string, number, or undefined/null)
|
|
|
|
|
* @returns Normalized string ID
|
|
|
|
|
* @throws Error if id is null or undefined
|
2026-01-13 18:47:57 +00:00
|
|
|
*
|
2025-12-25 13:27:28 +00:00
|
|
|
* @example
|
|
|
|
|
* ```typescript
|
|
|
|
|
* normalizeIdRequired(123) // "123"
|
|
|
|
|
* normalizeIdRequired("abc-123") // "abc-123"
|
|
|
|
|
* normalizeIdRequired(null) // throws Error
|
|
|
|
|
* ```
|
|
|
|
|
*/
|
2026-01-13 18:47:57 +00:00
|
|
|
export function normalizeIdRequired(
|
|
|
|
|
id: string | number | null | undefined,
|
|
|
|
|
): string {
|
2025-12-25 13:27:28 +00:00
|
|
|
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
|
2026-01-13 18:47:57 +00:00
|
|
|
*
|
2025-12-25 13:27:28 +00:00
|
|
|
* @param ids - Array of ID values
|
|
|
|
|
* @returns Array of normalized string IDs
|
2026-01-13 18:47:57 +00:00
|
|
|
*
|
2025-12-25 13:27:28 +00:00
|
|
|
* @example
|
|
|
|
|
* ```typescript
|
|
|
|
|
* normalizeIds([1, 2, "abc"]) // ["1", "2", "abc"]
|
|
|
|
|
* ```
|
|
|
|
|
*/
|
2026-01-13 18:47:57 +00:00
|
|
|
export function normalizeIds(
|
|
|
|
|
ids: (string | number | null | undefined)[],
|
|
|
|
|
): string[] {
|
2025-12-25 13:27:28 +00:00
|
|
|
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
|
2026-01-13 18:47:57 +00:00
|
|
|
*
|
2025-12-25 13:27:28 +00:00
|
|
|
* @param obj - Object to normalize
|
|
|
|
|
* @param idFields - Optional array of field names to normalize (default: common ID fields)
|
|
|
|
|
* @returns New object with normalized IDs
|
2026-01-13 18:47:57 +00:00
|
|
|
*
|
2025-12-25 13:27:28 +00:00
|
|
|
* @example
|
|
|
|
|
* ```typescript
|
2026-01-13 18:47:57 +00:00
|
|
|
* normalizeObjectIds({ id: 123, user_id: 456 })
|
2025-12-25 13:27:28 +00:00
|
|
|
* // { id: "123", user_id: "456" }
|
|
|
|
|
* ```
|
|
|
|
|
*/
|
|
|
|
|
export function normalizeObjectIds<T extends Record<string, any>>(
|
|
|
|
|
obj: T,
|
2026-01-13 18:47:57 +00:00
|
|
|
idFields: string[] = [
|
|
|
|
|
'id',
|
|
|
|
|
'user_id',
|
|
|
|
|
'track_id',
|
|
|
|
|
'playlist_id',
|
|
|
|
|
'conversation_id',
|
|
|
|
|
'message_id',
|
|
|
|
|
'sender_id',
|
|
|
|
|
'creator_id',
|
|
|
|
|
'created_by',
|
|
|
|
|
'parent_id',
|
|
|
|
|
'parent_message_id',
|
|
|
|
|
],
|
2025-12-25 13:27:28 +00:00
|
|
|
): T {
|
|
|
|
|
if (!obj || typeof obj !== 'object') {
|
|
|
|
|
return obj;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const normalized = { ...obj } as Record<string, any>;
|
|
|
|
|
|
|
|
|
|
for (const [key, value] of Object.entries(normalized)) {
|
|
|
|
|
// Normalize ID fields
|
|
|
|
|
if (idFields.includes(key)) {
|
|
|
|
|
normalized[key] = normalizeId(value);
|
|
|
|
|
}
|
|
|
|
|
// Recursively normalize nested objects
|
2026-01-13 18:47:57 +00:00
|
|
|
else if (
|
|
|
|
|
value &&
|
|
|
|
|
typeof value === 'object' &&
|
|
|
|
|
!Array.isArray(value) &&
|
|
|
|
|
!(value instanceof Date)
|
|
|
|
|
) {
|
2025-12-25 13:27:28 +00:00
|
|
|
normalized[key] = normalizeObjectIds(value, idFields);
|
|
|
|
|
}
|
|
|
|
|
// Normalize arrays of objects
|
2026-01-13 18:47:57 +00:00
|
|
|
else if (
|
|
|
|
|
Array.isArray(value) &&
|
|
|
|
|
value.length > 0 &&
|
|
|
|
|
typeof value[0] === 'object'
|
|
|
|
|
) {
|
|
|
|
|
normalized[key] = value.map((item) =>
|
|
|
|
|
typeof item === 'object' && item !== null
|
2025-12-25 13:27:28 +00:00
|
|
|
? normalizeObjectIds(item, idFields)
|
2026-01-13 18:47:57 +00:00
|
|
|
: item,
|
2025-12-25 13:27:28 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return normalized as T;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Normalize IDs in an array of objects
|
2026-01-13 18:47:57 +00:00
|
|
|
*
|
2025-12-25 13:27:28 +00:00
|
|
|
* @param items - Array of objects to normalize
|
|
|
|
|
* @param idFields - Optional array of field names to normalize
|
|
|
|
|
* @returns Array of objects with normalized IDs
|
2026-01-13 18:47:57 +00:00
|
|
|
*
|
2025-12-25 13:27:28 +00:00
|
|
|
* @example
|
|
|
|
|
* ```typescript
|
2026-01-13 18:47:57 +00:00
|
|
|
* normalizeArrayIds([{ id: 1 }, { id: 2 }])
|
2025-12-25 13:27:28 +00:00
|
|
|
* // [{ id: "1" }, { id: "2" }]
|
|
|
|
|
* ```
|
|
|
|
|
*/
|
|
|
|
|
export function normalizeArrayIds<T extends Record<string, any>>(
|
|
|
|
|
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;
|
|
|
|
|
}
|