refactor(federation): Make references always have domains
Some checks failed
CodeQL Scan / Analyze (push) Failing after 0s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
Build Docker Images / lint (push) Has been cancelled
Build Docker Images / check (push) Has been cancelled
Build Docker Images / tests (push) Has been cancelled
Build Docker Images / detect-circular (push) Has been cancelled
Mirror to Codeberg / Mirror (push) Has been cancelled
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been cancelled
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been cancelled

This commit is contained in:
Jesse Wierzbinski 2026-04-03 13:34:19 +02:00
parent df2a5ce260
commit 709e1c6087
No known key found for this signature in database
22 changed files with 688 additions and 477 deletions

View file

@ -7,25 +7,37 @@ import type { JSONObject } from "../types.ts";
import { Entity } from "./entity.ts";
export class Collection extends Entity {
public constructor(public override data: z.infer<typeof CollectionSchema>) {
super(data);
public constructor(
public override data: z.infer<typeof CollectionSchema>,
instanceDomain: string,
) {
super(data, instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<Collection> {
return CollectionSchema.parseAsync(json).then((u) => new Collection(u));
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<Collection> {
return CollectionSchema.parseAsync(json).then(
(u) => new Collection(u, instanceDomain),
);
}
}
export class URICollection extends Entity {
public constructor(
public override data: z.infer<typeof URICollectionSchema>,
instanceDomain: string,
) {
super(data);
super(data, instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<URICollection> {
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<URICollection> {
return URICollectionSchema.parseAsync(json).then(
(u) => new URICollection(u),
(u) => new URICollection(u, instanceDomain),
);
}
}

View file

@ -6,19 +6,27 @@ import { Entity, Reference } from "./entity.ts";
export class Delete extends Entity {
public static override name = "Delete";
public constructor(public override data: z.infer<typeof DeleteSchema>) {
super(data);
public constructor(
public override data: z.infer<typeof DeleteSchema>,
instanceDomain: string,
) {
super(data, instanceDomain);
}
public get author(): Reference {
return Reference.fromString(this.data.author);
return Reference.fromString(this.data.author, this.instanceDomain);
}
public get deleted(): Reference {
return Reference.fromString(this.data.deleted);
return Reference.fromString(this.data.deleted, this.instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<Delete> {
return DeleteSchema.parseAsync(json).then((u) => new Delete(u));
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<Delete> {
return DeleteSchema.parseAsync(json).then(
(u) => new Delete(u, instanceDomain),
);
}
}

View file

@ -4,11 +4,19 @@ import type { JSONObject } from "../types.ts";
export class Entity {
public static name = "Entity";
// biome-ignore lint/suspicious/noExplicitAny: This is a base class that is never instanciated directly
public constructor(public data: any) {}
public constructor(
// biome-ignore lint/suspicious/noExplicitAny: This is a base class that is never instanciated directly
public data: any,
public instanceDomain: string,
) {}
public static fromJSON(json: JSONObject): Promise<Entity> {
return EntitySchema.parseAsync(json).then((u) => new Entity(u));
public static fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<Entity> {
return EntitySchema.parseAsync(json).then(
(u) => new Entity(u, instanceDomain),
);
}
public toJSON(): JSONObject {
@ -19,10 +27,19 @@ export class Entity {
export class Reference {
public constructor(
public id: string,
public domain?: string,
public domain: string,
) {}
public static fromString(str: string): Reference {
/**
* Parses a reference from a string. The string can be in the format "domain:id" or just "id" if the domain is the local instance (in which case a default domain must be provided).
* @param str
* @param defaultDomain
* @returns
*/
public static fromString(
str: string,
defaultDomain: URL | string,
): Reference {
// Expect format: domain:id or id (if domain is the local instance)
// Handle IPv6 addresses in brackets
const chunks = str.split(":");
@ -36,10 +53,21 @@ export class Reference {
return new Reference(id, domain);
}
return new Reference(str);
if (!defaultDomain) {
throw new Error(
`Invalid reference string: ${str}. Expected format "domain:id" or "id" with a default domain provided.`,
);
}
return new Reference(
str,
defaultDomain instanceof URL
? defaultDomain.hostname
: defaultDomain,
);
}
public toString(): string {
return this.domain ? `${this.domain}:${this.id}` : this.id;
return `${this.domain}:${this.id}`;
}
}

View file

@ -6,39 +6,55 @@ import { Entity, Reference } from "../entity.ts";
export class Like extends Entity {
public static override name = "pub.versia:likes/Like";
public constructor(public override data: z.infer<typeof LikeSchema>) {
super(data);
public constructor(
public override data: z.infer<typeof LikeSchema>,
instanceDomain: string,
) {
super(data, instanceDomain);
}
public get author(): Reference {
return Reference.fromString(this.data.author);
return Reference.fromString(this.data.author, this.instanceDomain);
}
public get liked(): Reference {
return Reference.fromString(this.data.liked);
return Reference.fromString(this.data.liked, this.instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<Like> {
return LikeSchema.parseAsync(json).then((u) => new Like(u));
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<Like> {
return LikeSchema.parseAsync(json).then(
(u) => new Like(u, instanceDomain),
);
}
}
export class Dislike extends Entity {
public static override name = "pub.versia:likes/Dislike";
public constructor(public override data: z.infer<typeof DislikeSchema>) {
super(data);
public constructor(
public override data: z.infer<typeof DislikeSchema>,
instanceDomain: string,
) {
super(data, instanceDomain);
}
public get author(): Reference {
return Reference.fromString(this.data.author);
return Reference.fromString(this.data.author, this.instanceDomain);
}
public get disliked(): Reference {
return Reference.fromString(this.data.disliked);
return Reference.fromString(this.data.disliked, this.instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<Dislike> {
return DislikeSchema.parseAsync(json).then((u) => new Dislike(u));
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<Dislike> {
return DislikeSchema.parseAsync(json).then(
(u) => new Dislike(u, instanceDomain),
);
}
}

View file

@ -6,19 +6,27 @@ import { Entity, Reference } from "../entity.ts";
export class Vote extends Entity {
public static override name = "pub.versia:polls/Vote";
public constructor(public override data: z.infer<typeof VoteSchema>) {
super(data);
public constructor(
public override data: z.infer<typeof VoteSchema>,
instanceDomain: string,
) {
super(data, instanceDomain);
}
public get author(): Reference {
return Reference.fromString(this.data.author);
return Reference.fromString(this.data.author, this.instanceDomain);
}
public get poll(): Reference {
return Reference.fromString(this.data.poll);
return Reference.fromString(this.data.poll, this.instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<Vote> {
return VoteSchema.parseAsync(json).then((u) => new Vote(u));
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<Vote> {
return VoteSchema.parseAsync(json).then(
(u) => new Vote(u, instanceDomain),
);
}
}

View file

@ -6,19 +6,27 @@ import { Entity, Reference } from "../entity.ts";
export class Reaction extends Entity {
public static override name = "pub.versia:reactions/Reaction";
public constructor(public override data: z.infer<typeof ReactionSchema>) {
super(data);
public constructor(
public override data: z.infer<typeof ReactionSchema>,
instanceDomain: string,
) {
super(data, instanceDomain);
}
public get author(): Reference {
return Reference.fromString(this.data.author);
return Reference.fromString(this.data.author, this.instanceDomain);
}
public get object(): Reference {
return Reference.fromString(this.data.object);
return Reference.fromString(this.data.object, this.instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<Reaction> {
return ReactionSchema.parseAsync(json).then((u) => new Reaction(u));
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<Reaction> {
return ReactionSchema.parseAsync(json).then(
(u) => new Reaction(u, instanceDomain),
);
}
}

View file

@ -6,19 +6,31 @@ import { Entity, Reference } from "../entity.ts";
export class Report extends Entity {
public static override name = "pub.versia:reports/Report";
public constructor(public override data: z.infer<typeof ReportSchema>) {
super(data);
public constructor(
public override data: z.infer<typeof ReportSchema>,
instanceDomain: string,
) {
super(data, instanceDomain);
}
public get author(): Reference | null {
return this.data.author ? Reference.fromString(this.data.author) : null;
return this.data.author
? Reference.fromString(this.data.author, this.instanceDomain)
: null;
}
public get reported(): Reference[] {
return this.data.reported.map((r) => Reference.fromString(r));
return this.data.reported.map((r) =>
Reference.fromString(r, this.instanceDomain),
);
}
public static override fromJSON(json: JSONObject): Promise<Report> {
return ReportSchema.parseAsync(json).then((u) => new Report(u));
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<Report> {
return ReportSchema.parseAsync(json).then(
(u) => new Report(u, instanceDomain),
);
}
}

View file

@ -6,19 +6,27 @@ import { Entity, Reference } from "../entity.ts";
export class Share extends Entity {
public static override name = "pub.versia:share/Share";
public constructor(public override data: z.infer<typeof ShareSchema>) {
super(data);
public constructor(
public override data: z.infer<typeof ShareSchema>,
instanceDomain: string,
) {
super(data, instanceDomain);
}
public get author(): Reference {
return Reference.fromString(this.data.author);
return Reference.fromString(this.data.author, this.instanceDomain);
}
public get shared(): Reference {
return Reference.fromString(this.data.shared);
return Reference.fromString(this.data.shared, this.instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<Share> {
return ShareSchema.parseAsync(json).then((u) => new Share(u));
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<Share> {
return ShareSchema.parseAsync(json).then(
(u) => new Share(u, instanceDomain),
);
}
}

View file

@ -11,20 +11,28 @@ import { Entity, Reference } from "./entity.ts";
export class Follow extends Entity {
public static override name = "Follow";
public constructor(public override data: z.infer<typeof FollowSchema>) {
super(data);
public constructor(
public override data: z.infer<typeof FollowSchema>,
instanceDomain: string,
) {
super(data, instanceDomain);
}
public get author(): Reference {
return Reference.fromString(this.data.author);
return Reference.fromString(this.data.author, this.instanceDomain);
}
public get followee(): Reference {
return Reference.fromString(this.data.followee);
return Reference.fromString(this.data.followee, this.instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<Follow> {
return FollowSchema.parseAsync(json).then((u) => new Follow(u));
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<Follow> {
return FollowSchema.parseAsync(json).then(
(u) => new Follow(u, instanceDomain),
);
}
}
@ -33,21 +41,25 @@ export class FollowAccept extends Entity {
public constructor(
public override data: z.infer<typeof FollowAcceptSchema>,
instanceDomain: string,
) {
super(data);
super(data, instanceDomain);
}
public get author(): Reference {
return Reference.fromString(this.data.author);
return Reference.fromString(this.data.author, this.instanceDomain);
}
public get follower(): Reference {
return Reference.fromString(this.data.follower);
return Reference.fromString(this.data.follower, this.instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<FollowAccept> {
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<FollowAccept> {
return FollowAcceptSchema.parseAsync(json).then(
(u) => new FollowAccept(u),
(u) => new FollowAccept(u, instanceDomain),
);
}
}
@ -57,21 +69,25 @@ export class FollowReject extends Entity {
public constructor(
public override data: z.infer<typeof FollowRejectSchema>,
instanceDomain: string,
) {
super(data);
super(data, instanceDomain);
}
public get author(): Reference {
return Reference.fromString(this.data.author);
return Reference.fromString(this.data.author, this.instanceDomain);
}
public get follower(): Reference {
return Reference.fromString(this.data.follower);
return Reference.fromString(this.data.follower, this.instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<FollowReject> {
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<FollowReject> {
return FollowRejectSchema.parseAsync(json).then(
(u) => new FollowReject(u),
(u) => new FollowReject(u, instanceDomain),
);
}
}
@ -79,19 +95,27 @@ export class FollowReject extends Entity {
export class Unfollow extends Entity {
public static override name = "Unfollow";
public constructor(public override data: z.infer<typeof UnfollowSchema>) {
super(data);
public constructor(
public override data: z.infer<typeof UnfollowSchema>,
instanceDomain: string,
) {
super(data, instanceDomain);
}
public get author(): Reference {
return Reference.fromString(this.data.author);
return Reference.fromString(this.data.author, this.instanceDomain);
}
public get followee(): Reference {
return Reference.fromString(this.data.followee);
return Reference.fromString(this.data.followee, this.instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<Unfollow> {
return UnfollowSchema.parseAsync(json).then((u) => new Unfollow(u));
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<Unfollow> {
return UnfollowSchema.parseAsync(json).then(
(u) => new Unfollow(u, instanceDomain),
);
}
}

View file

@ -10,7 +10,7 @@ export class InstanceMetadata extends Entity {
public constructor(
public override data: z.infer<typeof InstanceMetadataSchema>,
) {
super(data);
super(data, data.domain);
}
public get logo(): ImageContentFormat | undefined {

View file

@ -7,16 +7,24 @@ import { Entity, Reference } from "./entity.ts";
export class Note extends Entity {
public static override name = "Note";
public constructor(public override data: z.infer<typeof NoteSchema>) {
super(data);
public constructor(
public override data: z.infer<typeof NoteSchema>,
instanceDomain: string,
) {
super(data, instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<Note> {
return NoteSchema.parseAsync(json).then((n) => new Note(n));
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<Note> {
return NoteSchema.parseAsync(json).then(
(n) => new Note(n, instanceDomain),
);
}
public get author(): Reference {
return Reference.fromString(this.data.author);
return Reference.fromString(this.data.author, this.instanceDomain);
}
public get group(): Reference | null {
@ -27,20 +35,24 @@ export class Note extends Entity {
return null;
}
return Reference.fromString(this.data.group);
return Reference.fromString(this.data.group, this.instanceDomain);
}
public get mentions(): Reference[] {
return this.data.mentions.map((m) => Reference.fromString(m));
return this.data.mentions.map((m) =>
Reference.fromString(m, this.instanceDomain),
);
}
public get quotes(): Reference | null {
return this.data.quotes ? Reference.fromString(this.data.quotes) : null;
return this.data.quotes
? Reference.fromString(this.data.quotes, this.instanceDomain)
: null;
}
public get repliesTo(): Reference | null {
return this.data.replies_to
? Reference.fromString(this.data.replies_to)
? Reference.fromString(this.data.replies_to, this.instanceDomain)
: null;
}

View file

@ -7,12 +7,20 @@ import { Entity } from "./entity.ts";
export class User extends Entity {
public static override name = "User";
public constructor(public override data: z.infer<typeof UserSchema>) {
super(data);
public constructor(
public override data: z.infer<typeof UserSchema>,
instanceDomain: string,
) {
super(data, instanceDomain);
}
public static override fromJSON(json: JSONObject): Promise<User> {
return UserSchema.parseAsync(json).then((u) => new User(u));
public static override fromJSON(
json: JSONObject,
instanceDomain: string,
): Promise<User> {
return UserSchema.parseAsync(json).then(
(u) => new User(u, instanceDomain),
);
}
public get avatar(): ImageContentFormat | undefined {