char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
92d1bb0c9
char/aliceai-t5-moe
1import { 2TRAILING_SLASHES_REGEX , 3TWO_PART_PUBLIC_SUFFIXES , 4WILDCARD_PUBLIC_SUFFIXES 5} from '$lib/constants' ; 6import { UrlProtocol } from '$lib/enums' ; 7 8/** 9* Check whether a hostname looks like an IPv4 or IPv6 address. 10*/ 11function isIpAddress ( hostname :string ) :boolean { 12if ( hostname . includes ( ':' )) return true ; 13 14if ( / ^\d{1,3}(\.\d{1,3}){3}$ / . test ( hostname )) return true ; 15 16return false ; 17} 18 19/** 20* Extract the registrable root domain from a URL. 21* 22* @example 23* 'mcp.example.com' -> 'example.com' 24* 'www.example.co.uk' -> 'example.co.uk' 25* 'bar.foo.nom.br' -> 'bar.foo.nom.br' 26* '192.168.1.1' -> null 27* 'localhost' -> null 28*/ 29export function extractRootDomain ( url :URL ) :string | null { 30const hostname = url . hostname . toLowerCase (); 31 32if ( ! hostname || isIpAddress ( hostname )) return null ; 33 34const parts = hostname . split ( '.' ); 35 36if ( parts . length < 2 ) return null ; 37 38if ( parts . length >= 3 ) { 39const suffix2 = ` ${ parts [ parts . length - 2 ]} . ${ parts [ parts . length - 1 ]} ` ; 40 41if ( TWO_PART_PUBLIC_SUFFIXES . has ( suffix2 )) { 42return parts . slice ( - 3 ). join ( '.' ); 43} 44} 45 46for ( let i = 2 ; i <= parts . length ; i ++ ) { 47const candidate = parts . slice ( - i ). join ( '.' ); 48 49if ( WILDCARD_PUBLIC_SUFFIXES . has ( candidate )) { 50if ( parts . length === i + 1 ) { 51return hostname ; 52} 53 54return parts . slice ( - ( i + 2 )). join ( '.' ); 55} 56} 57 58return parts . slice ( - 2 ). join ( '.' ); 59} 60 61/** 62* Sanitize an external URL string for safe use in an `<a href>`. 63* Only allows http: and https: schemes. Returns `null` for anything else. 64*/ 65export function sanitizeExternalUrl ( raw :string ) :string | null { 66try { 67const url = new URL ( raw ); 68 69if ( url . protocol !== UrlProtocol . HTTP && url . protocol !== UrlProtocol . HTTPS ) { 70return null ; 71} 72 73return url . href ; 74} catch { 75return null ; 76} 77} 78 79/** 80* Canonicalize a server URL for "is this the same server?" checks across 81* the user's settings and the recommended-server list. Lowercases scheme 82* and host, drops the port entirely, and strips any trailing slashes off 83* the path so a stored `https://api.example.com:8443/mcp/` matches the 84* recommended `https://api.example.com/mcp`. Falls back to a cheap 85* trim+lowercase+strip pass when the input isn't a parseable URL. 86* 87* Query strings are preserved deliberately - if the user entered one, 88* it's part of their endpoint. The port is always stripped because the 89* underlying `URL` parser is asymmetric (it auto-drops HTTPS default 90* :443 but keeps HTTP default :80), so a half-hearted "drop default 91* ports" policy never matches consistently across schemes. 92*/ 93export function canonicalizeServerUrl ( raw :string ) :string { 94const trimmed = raw . trim (); 95 96try { 97const parsed = new URL ( trimmed ); 98const pathname = parsed . pathname . replace ( TRAILING_SLASHES_REGEX , '' ); 99// Aggressive: drop the port unconditionally. We only use this for 100// equality checks between user-typed URLs and a hard-coded list of 101// recommendations, where the port can never carry distinguishing 102// information we care about (a different port = a different server, 103// but two URLs that differ only in `:80` vs no-port are clearly the 104// same intent). Lowercasing the hostname matches HTTP/HTTPS 105// case-insensitivity - the URL parser does NOT lowercase it. 106const host = parsed . hostname . toLowerCase (); 107 108return ` ${ parsed . protocol } // ${ host } ${ pathname } ${ parsed . search } ` ; 109} catch { 110return trimmed . toLowerCase (). replace ( TRAILING_SLASHES_REGEX , '' ); 111} 112}