1import 'identity_resolver_error.dart'; 2 3/// Normalizes a handle to lowercase. 4String normalizeHandle(String handle) => handle.toLowerCase(); 5 6/// Checks if a handle is valid according to atProto spec. 7/// 8/// A valid handle must: 9/// - Be between 1 and 253 characters 10/// - Match the pattern: subdomain.domain.tld 11/// - Each label must start and end with alphanumeric 12/// - Labels can contain hyphens but not at boundaries 13bool isValidHandle(String handle) { 14 if (handle.isEmpty || handle.length >= 254) return false; 15 16 // Pattern: ([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])? 17 final pattern = RegExp( 18 r'^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$', 19 ); 20 21 return pattern.hasMatch(handle); 22} 23 24/// Returns a normalized handle if valid, null otherwise. 25String? asNormalizedHandle(String input) { 26 final handle = normalizeHandle(input); 27 return isValidHandle(handle) ? handle : null; 28} 29 30/// Asserts that a handle is valid. 31void assertValidHandle(String handle) { 32 if (!isValidHandle(handle)) { 33 throw InvalidHandleError(handle, 'Invalid handle format'); 34 } 35}