A social knowledge tool for researchers built on ATProto
1import { ValueObject } from '../../../../shared/domain/ValueObject';
2import { Result, ok, err } from '../../../../shared/core/Result';
3
4interface UrlMetadataProps {
5 url: string;
6 title?: string;
7 description?: string;
8 author?: string;
9 publishedDate?: Date;
10 siteName?: string;
11 imageUrl?: string;
12 type?: string;
13 retrievedAt?: Date;
14}
15
16export class UrlMetadata extends ValueObject<UrlMetadataProps> {
17 get url(): string {
18 return this.props.url;
19 }
20
21 get title(): string | undefined {
22 return this.props.title;
23 }
24
25 get description(): string | undefined {
26 return this.props.description;
27 }
28
29 get author(): string | undefined {
30 return this.props.author;
31 }
32
33 get publishedDate(): Date | undefined {
34 return this.props.publishedDate;
35 }
36
37 get siteName(): string | undefined {
38 return this.props.siteName;
39 }
40
41 get imageUrl(): string | undefined {
42 return this.props.imageUrl;
43 }
44
45 get type(): string | undefined {
46 return this.props.type;
47 }
48
49 get retrievedAt(): Date | undefined {
50 return this.props.retrievedAt;
51 }
52
53 private constructor(props: UrlMetadataProps) {
54 super(props);
55 }
56
57 public static create(props: UrlMetadataProps): Result<UrlMetadata, Error> {
58 if (!props.url || props.url.trim().length === 0) {
59 return err(new Error('URL is required for metadata'));
60 }
61
62 return ok(
63 new UrlMetadata({
64 ...props,
65 retrievedAt: props.retrievedAt || new Date(),
66 }),
67 );
68 }
69
70 public isStale(maxAgeHours: number = 24): boolean {
71 if (!this.retrievedAt) {
72 return true; // If no retrievedAt, consider it stale
73 }
74 const ageInHours =
75 (Date.now() - this.retrievedAt.getTime()) / (1000 * 60 * 60);
76 return ageInHours > maxAgeHours;
77 }
78}