atproto explorer pdsls.dev
atproto tool
1type Did<TMethod extends string = string> = `did:${TMethod}:${string}`; 2 3type Nsid = `${string}.${string}.${string}`; 4 5type RecordKey = string; 6 7const DID_RE = /^did:([a-z]+):([a-zA-Z0-9._:%\-]*[a-zA-Z0-9._\-])$/; 8 9const NSID_RE = 10 /^[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?:\.[a-zA-Z](?:[a-zA-Z0-9]{0,62})?)$/; 11 12const RECORD_KEY_RE = /^(?!\.{1,2}$)[a-zA-Z0-9_~.:-]{1,512}$/; 13 14export const ATURI_RE = 15 /^at:\/\/([a-zA-Z0-9._:%-]+)(?:\/([a-zA-Z0-9-.]+)(?:\/([a-zA-Z0-9._~:@!$&%')(*+,;=-]+))?)?(?:#(\/[a-zA-Z0-9._~:@!$&%')(*+,;=\-[\]/\\]*))?$/; 16 17const isDid = (input: unknown): input is Did => { 18 return ( 19 typeof input === "string" && input.length >= 7 && input.length <= 2048 && DID_RE.test(input) 20 ); 21}; 22 23const isNsid = (input: unknown): input is Nsid => { 24 return ( 25 typeof input === "string" && input.length >= 5 && input.length <= 317 && NSID_RE.test(input) 26 ); 27}; 28 29const isRecordKey = (input: unknown): input is RecordKey => { 30 return ( 31 typeof input === "string" && 32 input.length >= 1 && 33 input.length <= 512 && 34 RECORD_KEY_RE.test(input) 35 ); 36}; 37 38export interface AddressedAtUri { 39 repo: Did; 40 collection: Nsid; 41 rkey: string; 42 fragment: string | undefined; 43} 44 45export const parseAddressedAtUri = (str: string): AddressedAtUri => { 46 const match = ATURI_RE.exec(str); 47 assert(match !== null, `invalid addressed-at-uri: ${str}`); 48 49 const [, r, c, k, f] = match; 50 assert(isDid(r), `invalid repo in addressed-at-uri: ${r}`); 51 assert(isNsid(c), `invalid collection in addressed-at-uri: ${c}`); 52 assert(isRecordKey(k), `invalid rkey in addressed-at-uri: ${k}`); 53 54 return { 55 repo: r, 56 collection: c, 57 rkey: k, 58 fragment: f, 59 }; 60}; 61 62function assert(condition: boolean, message: string): asserts condition { 63 if (!condition) { 64 throw new Error(message); 65 } 66}