export const camelCaseToDisplay = (camelCaseString: string): string => { return (camelCaseString.charAt(0).toUpperCase() + camelCaseString.slice(1)).replace(/([A-Z])/g, ' $1').trim(); }; export const snakeCaseToDisplay = (snakeCaseString: string): string => { return snakeCaseString.charAt(0).toUpperCase() + snakeCaseString.slice(1).replace('_', ' ').trim(); }; export const jsonPrettyPrint = (obj: unknown): string => { return JSON.stringify(obj, null, 2); }; export const EmailRegexString = '(([^<>()[\\].,;:\\s@\\"]+(\\.[^<>()[\\]\\.,;:\\s@\\"]+)*)|(\\".+\\"))@(([^<>()[\\]\\.,;:\\s@\\"]+\\.)+[^<>()[\\]\\.,;:\\s@\\"]{2,})'; const EmailRegexComplete = new RegExp(`^${EmailRegexString}$`); export const validateEmail = (email: string): boolean => { return EmailRegexComplete.test(email); }; // Matches canonical RFC 4122-style UUIDs (8-4-4-4-12 hex, case-insensitive). // Intentionally strict: we want to reject inputs that PostgreSQL would later // reject with `invalid input syntax for type uuid`, surfacing the boundary as // a 4xx in our own validators instead of a 500 from the DB driver. Do NOT // loosen this to a partial match or a "looks UUID-ish" regex — the strictness // is the contract. export const UUID_REGEX_STRING = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'; const UuidRegexComplete = new RegExp(`^${UUID_REGEX_STRING}$`, 'i'); export const isUuid = (value: unknown): value is string => { return typeof value === 'string' && UuidRegexComplete.test(value); }; export const getNextEntityName = (prefix: string, existingNames: string[], separator = '_') => { const cleanName = (name: string) => { return ( name // eslint-disable-next-line no-useless-escape .replace(/[\[\]\(\)\s]/g, separator) .replace(/^a-zA-Z0-9+/g, '') .replace(new RegExp(`${separator}+`, 'g'), separator) ); }; const formattedPrefix = cleanName(prefix); const cleanedNames = existingNames.map((name) => cleanName(name.trim())); const regex = new RegExp(`^${formattedPrefix}(\\d+)$`); const usedIndices: number[] = cleanedNames.map((name) => { if (name && regex.test(name)) { const matches = name.match(regex); const ind = matches && Array.isArray(matches) ? parseInt(matches[1], 10) : 0; return Number.isNaN(ind) ? 0 : ind; } return 0; }) as number[]; const lastIndex = Math.max(...usedIndices, ...[0]); return `${formattedPrefix}${lastIndex + 1}`; };