Suite of AT Protocol TypeScript libraries built on web standards
21
fork

Configure Feed

Select the types of activity you want to include in your feed.

at lex 154 lines 4.8 kB view raw
1export const INVALID_HANDLE = "handle.invalid"; 2 3/** Registration-time restrictions, not protocol-level restrictions. 4 * `.test` is allowed but only should be used in testing and development. 5 * @see {https://en.wikipedia.org/wiki/Top-level_domain#Reserved_domains} 6 */ 7export const DISALLOWED_TLDS = [ 8 ".local", 9 ".arpa", 10 ".invalid", 11 ".localhost", 12 ".internal", 13 ".example", 14 ".alt", 15 // policy could concievably change on ".onion" some day 16 ".onion", 17]; 18 19/** 20 * Ensure a handle is valid 21 * @throws If handle is invalid 22 * 23 * Handle constraints, in English: 24 * - must be a possible domain name 25 * - RFC-1035 is commonly referenced, but has been updated. eg, RFC-3696, 26 * section 2. and RFC-3986, section 3. can now have leading numbers (eg, 27 * 4chan.org) 28 * - "labels" (sub-names) are made of ASCII letters, digits, hyphens 29 * - can not start or end with a hyphen 30 * - TLD (last component) should not start with a digit 31 * - can't end with a hyphen (can end with digit) 32 * - each segment must be between 1 and 63 characters (not including any periods) 33 * - overall length can't be more than 253 characters 34 * - separated by (ASCII) periods; does not start or end with period 35 * - case insensitive 36 * - domains (handles) are equal if they are the same lower-case 37 * - punycode allowed for internationalization 38 * - no whitespace, null bytes, joining chars, etc 39 * - does not validate whether domain or TLD exists, or is a reserved or 40 * special TLD (eg, .onion or .local) 41 * - does not validate punycode 42 */ 43export const ensureValidHandle = (handle: string): void => { 44 // check that all chars are boring ASCII 45 if (!/^[a-zA-Z0-9.-]*$/.test(handle)) { 46 throw new InvalidHandleError( 47 "Disallowed characters in handle (ASCII letters, digits, dashes, periods only)", 48 ); 49 } 50 51 if (handle.length > 253) { 52 throw new InvalidHandleError("Handle is too long (253 chars max)"); 53 } 54 const labels = handle.split("."); 55 if (labels.length < 2) { 56 throw new InvalidHandleError("Handle domain needs at least two parts"); 57 } 58 for (let i = 0; i < labels.length; i++) { 59 const l = labels[i]; 60 if (l.length < 1) { 61 throw new InvalidHandleError("Handle parts can not be empty"); 62 } 63 if (l.length > 63) { 64 throw new InvalidHandleError("Handle part too long (max 63 chars)"); 65 } 66 if (l.endsWith("-") || l.startsWith("-")) { 67 throw new InvalidHandleError( 68 "Handle parts can not start or end with hyphens", 69 ); 70 } 71 if (i + 1 === labels.length && !/^[a-zA-Z]/.test(l)) { 72 throw new InvalidHandleError( 73 "Handle final component (TLD) must start with ASCII letter", 74 ); 75 } 76 } 77}; 78 79/** 80 * Ensure a handle is valid using a regex pattern. 81 * @throws If handle is invalid 82 */ 83export const ensureValidHandleRegex = (handle: string): void => { 84 if ( 85 !/^([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])?$/ 86 .test( 87 handle, 88 ) 89 ) { 90 throw new InvalidHandleError("Handle didn't validate via regex"); 91 } 92 if (handle.length > 253) { 93 throw new InvalidHandleError("Handle is too long (253 chars max)"); 94 } 95}; 96 97/** 98 * Converts a handle to lowercase. 99 */ 100export const normalizeHandle = (handle: string): string => { 101 return handle.toLowerCase(); 102}; 103 104/** 105 * Converts a handle to lowercase and ensures it is valid. 106 * @returns The normalized handle if it is valid 107 * @throws If handle is invalid 108 */ 109export const normalizeAndEnsureValidHandle = (handle: string): string => { 110 const normalized = normalizeHandle(handle); 111 ensureValidHandle(normalized); 112 return normalized; 113}; 114 115/** 116 * Checks if a handle is valid and returns a boolean. 117 * 118 * @returns True if handle is valid 119 */ 120export const isValidHandle = (handle: string): boolean => { 121 try { 122 ensureValidHandle(handle); 123 } catch (err) { 124 if (err instanceof InvalidHandleError) { 125 return false; 126 } 127 throw err; 128 } 129 130 return true; 131}; 132 133/** 134 * Check if a TLD is valid. 135 * 136 * Disallowed TLDs: {@linkcode DISALLOWED_TLDS} 137 */ 138export const isValidTld = (handle: string): boolean => { 139 return !DISALLOWED_TLDS.some((domain) => handle.endsWith(domain)); 140}; 141 142/** 143 * Thrown when a handle is invalid. 144 * Caused by invalid characters (only ASCII letters, digits, dashes, periods are allowed), 145 * length longer than 253 characters, or one of the {@linkcode DISALLOWED_TLDS} used. 146 */ 147export class InvalidHandleError extends Error {} 148 149/** @deprecated Use {@linkcode InvalidHandleError} */ 150export class ReservedHandleError extends Error {} 151/** @deprecated Use {@linkcode InvalidHandleError} */ 152export class UnsupportedDomainError extends Error {} 153/** @deprecated Use {@linkcode InvalidHandleError} */ 154export class DisallowedDomainError extends Error {}