[WIP] A (somewhat barebones) atproto app for creating custom sites without hosting!

server: define url <=> rkey conversion syntax

Changed files
+82
src
+82
src/user.ts
··· 23 23 web: new WebDidDocumentResolver(), 24 24 }, 25 25 }); 26 + 27 + /** 28 + * given a valid url path string containing 29 + * - `/` for seperating characters 30 + * - a-zA-Z0-9 `-._~` as unreserved 31 + * - `!$&'()*+,;=` as reserved but valid in paths 32 + * - `:@` as neither reserved or unreserved but valid in paths 33 + * - %XX where X are hex digits for percent encoding 34 + * 35 + * we need to consistently and bidirectionally convert it into a string containing the characters A-Z, a-z, 0-9, `.-_:~` for an atproto rkey 36 + * A-Z a-z 0-9 are covered easily 37 + * we can also take -._~ as they are also unreserved 38 + * leaving : as a valid rkey character which looks nice for encoding 39 + * the uppercase versions MUST be used to prevent ambiguity 40 + * a colon which isnt followed by a valid character is an invalid rkey and should be ignored 41 + * - `/` `::` 42 + * - `%` `:~` 43 + * - `!` `:21` 44 + * - `$` `:24` 45 + * - `&` `:26` 46 + * - `'` `:27` 47 + * - `(` `:28` 48 + * - `)` `:29` 49 + * - `*` `:2A` 50 + * - `+` `:2B` 51 + * - `,` `:2C` 52 + * - `:` `:3A` 53 + * - `;` `:3B` 54 + * - `=` `:3D` 55 + * - `@` `:40` 56 + * @returns {string | undefined} undefined when input is invalid 57 + */ 58 + function urlToRkey(url: string): string | undefined { 59 + // contains 0-9A-Za-z + special valid chars and / seperator. also can contain %XX with XX being hex 60 + if (!url.match(/^([a-zA-Z0-9/\-._~!$&'()*+,;=:@]|(%[0-9a-fA-F]{2}))*$/gm)) 61 + return; 62 + return ( 63 + url 64 + // : replace is hoisted so it doesnt replace colons from elsewhere 65 + .replaceAll(":", ":3A") 66 + .replaceAll("/", "::") 67 + .replaceAll("%", ":~") 68 + .replaceAll("!", ":21") 69 + .replaceAll("$", ":24") 70 + .replaceAll("&", ":26") 71 + .replaceAll("'", ":27") 72 + .replaceAll("(", ":28") 73 + .replaceAll(")", ":29") 74 + .replaceAll("*", ":2A") 75 + .replaceAll("+", ":2B") 76 + .replaceAll(",", ":2C") 77 + .replaceAll(";", ":3B") 78 + .replaceAll("=", ":3D") 79 + .replaceAll("@", ":40") 80 + ); 81 + } 82 + 83 + /** 84 + * @see {@link urlToRkey} for rkey <=> url conversion syntax 85 + * @returns {string | undefined} undefined when input is invalid 86 + */ 87 + function rkeyToUrl(rkey: string): string | undefined { 88 + // contains 0-9A-Za-z .-_:~ 89 + if (!rkey.match(/^[A-Za-z0-9.\-_:~]*$/gm)) return; 90 + return rkey 91 + .replaceAll("::", "/") 92 + .replaceAll(":~", "%") 93 + .replaceAll(":21", "!") 94 + .replaceAll(":24", "$") 95 + .replaceAll(":26", "&") 96 + .replaceAll(":27", "'") 97 + .replaceAll(":28", "(") 98 + .replaceAll(":29", ")") 99 + .replaceAll(":2A", "*") 100 + .replaceAll(":2B", "+") 101 + .replaceAll(":2C", ",") 102 + .replaceAll(":3A", ":") 103 + .replaceAll(":3B", ";") 104 + .replaceAll(":3D", "=") 105 + .replaceAll(":40", "@"); 106 + } 107 + 26 108 async function getRoute( 27 109 did: `did:${"plc" | "web"}:${string}`, 28 110 pds: string,