This repository has no description
0

Configure Feed

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

semble / src / modules / atproto / domain / ATUri.ts
2.7 kB 122 lines
1import { ValueObject } from '../../../shared/domain/ValueObject'; 2import { Result, ok, err } from '../../../shared/core/Result'; 3import { DID } from './DID'; 4 5export class InvalidATUriError extends Error { 6 constructor(message: string) { 7 super(message); 8 this.name = 'InvalidATUriError'; 9 } 10} 11 12interface ATUriProps { 13 value: string; 14 did: DID; 15 collection: string; 16 rkey: string; 17} 18 19export class ATUri extends ValueObject<ATUriProps> { 20 get value(): string { 21 return this.props.value; 22 } 23 24 get did(): DID { 25 return this.props.did; 26 } 27 28 get collection(): string { 29 return this.props.collection; 30 } 31 32 get rkey(): string { 33 return this.props.rkey; 34 } 35 36 private constructor(props: ATUriProps) { 37 super(props); 38 } 39 40 public static create(uri: string): Result<ATUri, InvalidATUriError> { 41 if (!uri || uri.trim().length === 0) { 42 return err(new InvalidATUriError('AT URI cannot be empty')); 43 } 44 45 const trimmedUri = uri.trim(); 46 47 if (!trimmedUri.startsWith('at://')) { 48 return err(new InvalidATUriError('AT URI must start with "at://"')); 49 } 50 51 const parts = trimmedUri.split('/'); 52 if (parts.length !== 5) { 53 return err( 54 new InvalidATUriError( 55 'AT URI must have exactly 5 parts separated by "/"', 56 ), 57 ); 58 } 59 60 const didResult = DID.create(parts[2]!); 61 if (didResult.isErr()) { 62 return err( 63 new InvalidATUriError( 64 `Invalid DID in AT URI: ${didResult.error.message}`, 65 ), 66 ); 67 } 68 69 const collection = parts[3]!; 70 const rkey = parts[4]!; 71 72 if (!collection || collection.length === 0) { 73 return err(new InvalidATUriError('Collection cannot be empty')); 74 } 75 76 if (!rkey || rkey.length === 0) { 77 return err(new InvalidATUriError('Record key cannot be empty')); 78 } 79 80 return ok( 81 new ATUri({ 82 value: trimmedUri, 83 did: didResult.value, 84 collection, 85 rkey, 86 }), 87 ); 88 } 89 90 public static fromParts( 91 did: DID, 92 collection: string, 93 rkey: string, 94 ): Result<ATUri, InvalidATUriError> { 95 if (!collection || collection.length === 0) { 96 return err(new InvalidATUriError('Collection cannot be empty')); 97 } 98 99 if (!rkey || rkey.length === 0) { 100 return err(new InvalidATUriError('Record key cannot be empty')); 101 } 102 103 const uri = `at://${did.value}/${collection}/${rkey}`; 104 105 return ok( 106 new ATUri({ 107 value: uri, 108 did, 109 collection, 110 rkey, 111 }), 112 ); 113 } 114 115 public toString(): string { 116 return this.props.value; 117 } 118 119 public equals(other: ATUri): boolean { 120 return this.props.value === other.props.value; 121 } 122}