diff --git a/packages/api/routes/versia/v0.6/[entity_type]/[id]/collections/[collection_type].ts b/packages/api/routes/versia/v0.6/[entity_type]/[id]/collections/[collection_type].ts
index d4e5a217..9236a65e 100644
--- a/packages/api/routes/versia/v0.6/[entity_type]/[id]/collections/[collection_type].ts
+++ b/packages/api/routes/versia/v0.6/[entity_type]/[id]/collections/[collection_type].ts
@@ -4,6 +4,7 @@ import {
EntitySchema,
URICollectionSchema,
} from "@versia/sdk/schemas";
+import { config } from "@versia-server/config";
import { ApiError } from "@versia-server/kit";
import { apiRoute, handleZodError } from "@versia-server/kit/api";
import { db, Instance, Note, User } from "@versia-server/kit/db";
@@ -112,13 +113,16 @@ export default apiRoute((app) =>
),
);
- entity = new VersiaEntities.URICollection({
- author: note.author.id,
- total: replyCount,
- items: replies.map((reply) =>
- reply.reference.toString(),
- ),
- });
+ entity = new VersiaEntities.URICollection(
+ {
+ author: note.author.id,
+ total: replyCount,
+ items: replies.map((reply) =>
+ reply.reference.toString(),
+ ),
+ },
+ config.http.base_url.hostname,
+ );
break;
}
case "quotes": {
@@ -146,13 +150,16 @@ export default apiRoute((app) =>
),
);
- entity = new VersiaEntities.URICollection({
- author: note.author.id,
- total: quoteCount,
- items: quotes.map((quote) =>
- quote.reference.toString(),
- ),
- });
+ entity = new VersiaEntities.URICollection(
+ {
+ author: note.author.id,
+ total: quoteCount,
+ items: quotes.map((quote) =>
+ quote.reference.toString(),
+ ),
+ },
+ config.http.base_url.hostname,
+ );
break;
}
case "pub.versia:share/Shares": {
@@ -180,13 +187,16 @@ export default apiRoute((app) =>
),
);
- entity = new VersiaEntities.URICollection({
- author: note.author.id,
- total: shareCount,
- items: shares.map((share) =>
- share.reference.toString(),
- ),
- });
+ entity = new VersiaEntities.URICollection(
+ {
+ author: note.author.id,
+ total: shareCount,
+ items: shares.map((share) =>
+ share.reference.toString(),
+ ),
+ },
+ config.http.base_url.hostname,
+ );
break;
}
}
@@ -226,23 +236,29 @@ export default apiRoute((app) =>
offset,
);
- entity = new VersiaEntities.Collection({
- author: user.id,
- total,
- items: outboxItems.map((note) =>
- note.toVersia(),
- ),
- });
+ entity = new VersiaEntities.Collection(
+ {
+ author: user.id,
+ total,
+ items: outboxItems.map((note) =>
+ note.toVersia(),
+ ),
+ },
+ config.http.base_url.hostname,
+ );
break;
}
case "followers": {
if (user.data.isHidingCollections) {
- entity = new VersiaEntities.URICollection({
- author: user.id,
- items: [],
- total: 0,
- });
+ entity = new VersiaEntities.URICollection(
+ {
+ author: user.id,
+ items: [],
+ total: 0,
+ },
+ config.http.base_url.hostname,
+ );
break;
}
@@ -258,23 +274,29 @@ export default apiRoute((app) =>
offset,
);
- entity = new VersiaEntities.URICollection({
- author: user.id,
- items: followers.map((follower) =>
- follower.reference.toString(),
- ),
- total,
- });
+ entity = new VersiaEntities.URICollection(
+ {
+ author: user.id,
+ items: followers.map((follower) =>
+ follower.reference.toString(),
+ ),
+ total,
+ },
+ config.http.base_url.hostname,
+ );
break;
}
case "following": {
if (user.data.isHidingCollections) {
- entity = new VersiaEntities.URICollection({
- author: user.id,
- items: [],
- total: 0,
- });
+ entity = new VersiaEntities.URICollection(
+ {
+ author: user.id,
+ items: [],
+ total: 0,
+ },
+ config.http.base_url.hostname,
+ );
break;
}
@@ -290,13 +312,16 @@ export default apiRoute((app) =>
offset,
);
- entity = new VersiaEntities.URICollection({
- author: user.id,
- items: following.map((followed) =>
- followed.reference.toString(),
- ),
- total,
- });
+ entity = new VersiaEntities.URICollection(
+ {
+ author: user.id,
+ items: following.map((followed) =>
+ followed.reference.toString(),
+ ),
+ total,
+ },
+ config.http.base_url.hostname,
+ );
break;
}
diff --git a/packages/api/routes/versia/v0.6/inbox.test.ts b/packages/api/routes/versia/v0.6/inbox.test.ts
index bcf4d992..af4f475f 100644
--- a/packages/api/routes/versia/v0.6/inbox.test.ts
+++ b/packages/api/routes/versia/v0.6/inbox.test.ts
@@ -83,15 +83,18 @@ mock(new URL(`/.versia/v0.6/entities/User/${userId}`, instanceUrl).href, {
headers: {
"Content-Type": "application/vnd.versia+json; charset=utf-8",
},
- data: new VersiaEntities.User({
- id: userId,
- created_at: "2025-04-18T10:32:01.427Z",
- type: "User",
- username: "testuser",
- fields: [],
- manually_approves_followers: false,
- indexable: true,
- }).toJSON(),
+ data: new VersiaEntities.User(
+ {
+ id: userId,
+ created_at: "2025-04-18T10:32:01.427Z",
+ type: "User",
+ username: "testuser",
+ fields: [],
+ manually_approves_followers: false,
+ indexable: true,
+ },
+ instanceUrl.hostname,
+ ).toJSON(),
},
});
@@ -111,35 +114,38 @@ afterAll(async () => {
describe("Inbox Tests", () => {
test("should correctly process inbox request", async () => {
- const exampleNote = new VersiaEntities.Note({
- id: noteId,
- created_at: "2025-04-18T10:32:01.427Z",
- type: "Note",
- extensions: {
- "pub.versia:custom_emojis": {
- emojis: [],
+ const exampleNote = new VersiaEntities.Note(
+ {
+ id: noteId,
+ created_at: "2025-04-18T10:32:01.427Z",
+ type: "Note",
+ extensions: {
+ "pub.versia:custom_emojis": {
+ emojis: [],
+ },
},
+ previews: [],
+ attachments: [],
+ author: userId,
+ content: {
+ "text/html": {
+ content: "
Hello!
",
+ remote: false,
+ },
+ "text/plain": {
+ content: "Hello!",
+ remote: false,
+ },
+ },
+ group: "public",
+ is_sensitive: false,
+ mentions: [],
+ quotes: null,
+ replies_to: null,
+ subject: "",
},
- previews: [],
- attachments: [],
- author: userId,
- content: {
- "text/html": {
- content: "Hello!
",
- remote: false,
- },
- "text/plain": {
- content: "Hello!",
- remote: false,
- },
- },
- group: "public",
- is_sensitive: false,
- mentions: [],
- quotes: null,
- replies_to: null,
- subject: "",
- });
+ instanceUrl.hostname,
+ );
const signedRequest = await sign(
instanceKeys.privateKey,
@@ -177,13 +183,16 @@ describe("Inbox Tests", () => {
});
test("should correctly process Share", async () => {
- const exampleRequest = new VersiaEntities.Share({
- id: shareId,
- created_at: "2025-04-18T10:32:01.427Z",
- type: "pub.versia:share/Share",
- author: userId,
- shared: noteId,
- });
+ const exampleRequest = new VersiaEntities.Share(
+ {
+ id: shareId,
+ created_at: "2025-04-18T10:32:01.427Z",
+ type: "pub.versia:share/Share",
+ author: userId,
+ shared: noteId,
+ },
+ instanceUrl.hostname,
+ );
const signedRequest = await sign(
instanceKeys.privateKey,
@@ -229,14 +238,17 @@ describe("Inbox Tests", () => {
});
test("should correctly process Reaction", async () => {
- const exampleRequest = new VersiaEntities.Reaction({
- id: reactionId,
- created_at: "2025-04-18T10:32:01.427Z",
- type: "pub.versia:reactions/Reaction",
- author: userId,
- object: noteId,
- content: "👍",
- });
+ const exampleRequest = new VersiaEntities.Reaction(
+ {
+ id: reactionId,
+ created_at: "2025-04-18T10:32:01.427Z",
+ type: "pub.versia:reactions/Reaction",
+ author: userId,
+ object: noteId,
+ content: "👍",
+ },
+ instanceUrl.hostname,
+ );
const signedRequest = await sign(
instanceKeys.privateKey,
@@ -304,34 +316,37 @@ describe("Inbox Tests", () => {
});
test("should correctly process Reaction with custom emoji", async () => {
- const exampleRequest = new VersiaEntities.Reaction({
- id: reaction2Id,
- created_at: "2025-04-18T10:32:01.427Z",
- type: "pub.versia:reactions/Reaction",
- author: userId,
- object: noteId,
- content: ":neocat:",
- extensions: {
- "pub.versia:custom_emojis": {
- emojis: [
- {
- name: ":neocat:",
- url: {
- "image/webp": {
- hash: "e06240155d2cb90e8dc05327d023585ab9d47216ff547ad72aaf75c485fe9649",
- size: 4664,
- width: 256,
- height: 256,
- remote: true,
- content:
- "https://cdn.cpluspatch.com/versia-cpp/e06240155d2cb90e8dc05327d023585ab9d47216ff547ad72aaf75c485fe9649/neocat.webp",
+ const exampleRequest = new VersiaEntities.Reaction(
+ {
+ id: reaction2Id,
+ created_at: "2025-04-18T10:32:01.427Z",
+ type: "pub.versia:reactions/Reaction",
+ author: userId,
+ object: noteId,
+ content: ":neocat:",
+ extensions: {
+ "pub.versia:custom_emojis": {
+ emojis: [
+ {
+ name: ":neocat:",
+ url: {
+ "image/webp": {
+ hash: "e06240155d2cb90e8dc05327d023585ab9d47216ff547ad72aaf75c485fe9649",
+ size: 4664,
+ width: 256,
+ height: 256,
+ remote: true,
+ content:
+ "https://cdn.cpluspatch.com/versia-cpp/e06240155d2cb90e8dc05327d023585ab9d47216ff547ad72aaf75c485fe9649/neocat.webp",
+ },
},
},
- },
- ],
+ ],
+ },
},
},
- });
+ instanceUrl.hostname,
+ );
const signedRequest = await sign(
instanceKeys.privateKey,
@@ -405,13 +420,16 @@ describe("Inbox Tests", () => {
expect(noteToDelete).not.toBeNull();
// Create a Delete request
- const exampleRequest = new VersiaEntities.Delete({
- created_at: new Date().toISOString(),
- type: "Delete",
- author: userId,
- deleted_type: "Note",
- deleted: noteId,
- });
+ const exampleRequest = new VersiaEntities.Delete(
+ {
+ created_at: new Date().toISOString(),
+ type: "Delete",
+ author: userId,
+ deleted_type: "Note",
+ deleted: noteId,
+ },
+ instanceUrl.hostname,
+ );
const signedRequest = await sign(
instanceKeys.privateKey,
diff --git a/packages/kit/db/like.ts b/packages/kit/db/like.ts
index 9b8072c2..9cbcddbc 100644
--- a/packages/kit/db/like.ts
+++ b/packages/kit/db/like.ts
@@ -1,4 +1,5 @@
import * as VersiaEntities from "@versia/sdk/entities";
+import { config } from "@versia-server/config";
import {
and,
desc,
@@ -20,7 +21,9 @@ import { BaseInterface } from "./base.ts";
import type { User } from "./user.ts";
type LikeType = InferSelectModel & {
- liker: InferSelectModel;
+ liker: InferSelectModel & {
+ instance: InferSelectModel | null;
+ };
liked: InferSelectModel & {
author: InferSelectModel & {
instance: InferSelectModel | null;
@@ -70,7 +73,11 @@ export class Like extends BaseInterface {
},
},
},
- liker: true,
+ liker: {
+ with: {
+ instance: true,
+ },
+ },
},
});
@@ -102,7 +109,11 @@ export class Like extends BaseInterface {
},
},
},
- liker: true,
+ liker: {
+ with: {
+ instance: true,
+ },
+ },
},
});
@@ -167,22 +178,28 @@ export class Like extends BaseInterface {
likedReference = `${this.data.liked.author.instance.domain}:${this.data.liked.remoteId}`;
}
- return new VersiaEntities.Like({
- id: this.id,
- author: this.data.liker.id,
- type: "pub.versia:likes/Like",
- created_at: this.data.createdAt.toISOString(),
- liked: likedReference,
- });
+ return new VersiaEntities.Like(
+ {
+ id: this.id,
+ author: this.data.liker.id,
+ type: "pub.versia:likes/Like",
+ created_at: this.data.createdAt.toISOString(),
+ liked: likedReference,
+ },
+ this.data.liker.instance?.domain ?? config.http.base_url.hostname,
+ );
}
public unlikeToVersia(unliker?: User): VersiaEntities.Delete {
- return new VersiaEntities.Delete({
- type: "Delete",
- created_at: new Date().toISOString(),
- author: unliker ? unliker.id : this.data.liker.id,
- deleted_type: "pub.versia:likes/Like",
- deleted: this.id,
- });
+ return new VersiaEntities.Delete(
+ {
+ type: "Delete",
+ created_at: new Date().toISOString(),
+ author: unliker ? unliker.id : this.data.liker.id,
+ deleted_type: "pub.versia:likes/Like",
+ deleted: this.id,
+ },
+ this.data.liker.instance?.domain ?? config.http.base_url.hostname,
+ );
}
}
diff --git a/packages/kit/db/note.ts b/packages/kit/db/note.ts
index 24bc4932..8d84007f 100644
--- a/packages/kit/db/note.ts
+++ b/packages/kit/db/note.ts
@@ -426,13 +426,13 @@ export class Note extends BaseInterface {
public get reference(): VersiaEntities.Reference {
if (this.remote) {
- const instanceUrl = new URL(
+ return new VersiaEntities.Reference(
+ this.id,
this.author.data.instance?.domain || "",
);
- return new VersiaEntities.Reference(this.id, instanceUrl.hostname);
}
- return new VersiaEntities.Reference(this.id);
+ return new VersiaEntities.Reference(this.id, config.http.base_url.host);
}
public async federateToUsers(): Promise {
@@ -941,19 +941,13 @@ export class Note extends BaseInterface {
*/
public static async resolve(
reference: VersiaEntities.Reference,
- defaultInstance?: Instance,
): Promise {
// Check if note not already in database
- if (
- !(reference.domain || defaultInstance) ||
- reference.domain === config.http.base_url.hostname
- ) {
+ if (reference.domain === config.http.base_url.hostname) {
return await Note.fromId(reference.id);
}
- const instance = reference.domain
- ? await Instance.resolve(reference.domain)
- : (defaultInstance as Instance);
+ const instance = await Instance.resolve(reference.domain);
const foundNote = await Note.fromSql(
and(
@@ -973,14 +967,7 @@ export class Note extends BaseInterface {
return foundNote;
}
- return Note.fromVersia(
- reference.domain
- ? reference
- : new VersiaEntities.Reference(
- reference.id,
- instance.data.domain,
- ),
- );
+ return Note.fromVersia(reference);
}
/**
@@ -1003,12 +990,6 @@ export class Note extends BaseInterface {
instance?: Instance,
): Promise {
if (versiaNote instanceof VersiaEntities.Reference) {
- if (!versiaNote.domain) {
- throw new Error(
- "Cannot fetch Versia note from reference without domain",
- );
- }
-
// No bridge support for notes yet
const note = await Instance.federationRequester.fetchEntity(
versiaNote,
@@ -1027,7 +1008,7 @@ export class Note extends BaseInterface {
const { created_at, extensions, group, id, is_sensitive, subject } =
versiaNote.data;
- const author = await User.resolve(versiaNote.author, instance);
+ const author = await User.resolve(versiaNote.author);
if (!author) {
throw new Error("Entity author could not be resolved");
@@ -1064,7 +1045,7 @@ export class Note extends BaseInterface {
const mentions = (
await Promise.all(
- versiaNote.mentions.map((m) => User.resolve(m, instance)) ?? [],
+ versiaNote.mentions.map((m) => User.resolve(m)) ?? [],
)
).filter((m) => m !== null);
@@ -1075,10 +1056,10 @@ export class Note extends BaseInterface {
: (group as "public" | "followers" | "unlisted");
const reply = versiaNote.repliesTo
- ? await Note.resolve(versiaNote.repliesTo, instance)
+ ? await Note.resolve(versiaNote.repliesTo)
: null;
const quote = versiaNote.quotes
- ? await Note.resolve(versiaNote.quotes, instance)
+ ? await Note.resolve(versiaNote.quotes)
: null;
const spoiler = subject ? await sanitizedHtmlStrip(subject) : undefined;
@@ -1296,13 +1277,16 @@ export class Note extends BaseInterface {
}
public deleteToVersia(): VersiaEntities.Delete {
- return new VersiaEntities.Delete({
- type: "Delete",
- author: this.author.id,
- deleted_type: "Note",
- deleted: this.id,
- created_at: new Date().toISOString(),
- });
+ return new VersiaEntities.Delete(
+ {
+ type: "Delete",
+ author: this.author.id,
+ deleted_type: "Note",
+ deleted: this.id,
+ created_at: new Date().toISOString(),
+ },
+ this.data.author.instance?.domain ?? config.http.base_url.hostname,
+ );
}
/**
@@ -1324,48 +1308,51 @@ export class Note extends BaseInterface {
replyReference = `${status.reply.author.instance.domain}:${status.reply.remoteId}`;
}
- return new VersiaEntities.Note({
- type: "Note",
- created_at: status.createdAt.toISOString(),
- id: status.id,
- author: this.author.id,
- content: {
- "text/html": {
- content: status.content,
- remote: false,
+ return new VersiaEntities.Note(
+ {
+ type: "Note",
+ created_at: status.createdAt.toISOString(),
+ id: status.id,
+ author: this.author.id,
+ content: {
+ "text/html": {
+ content: status.content,
+ remote: false,
+ },
+ "text/plain": {
+ content: htmlToText(status.content),
+ remote: false,
+ },
},
- "text/plain": {
- content: htmlToText(status.content),
- remote: false,
+ previews: [],
+ attachments: status.attachments.map(
+ (attachment) =>
+ new Media(attachment).toVersia().data as z.infer<
+ typeof NonTextContentFormatSchema
+ >,
+ ),
+ is_sensitive: status.sensitive,
+ mentions: status.mentions.map((mention) =>
+ mention.instance
+ ? `${mention.instance.domain}:${mention.id}`
+ : mention.id,
+ ),
+ quotes: quoteReference,
+ replies_to: replyReference,
+ subject: status.spoilerText,
+ // TODO: Refactor as part of groups
+ group: status.visibility === "public" ? "public" : "followers",
+ extensions: {
+ "pub.versia:custom_emojis": {
+ emojis: status.emojis.map((emoji) =>
+ new Emoji(emoji).toVersia(),
+ ),
+ },
+ // TODO: Add polls and reactions
},
},
- previews: [],
- attachments: status.attachments.map(
- (attachment) =>
- new Media(attachment).toVersia().data as z.infer<
- typeof NonTextContentFormatSchema
- >,
- ),
- is_sensitive: status.sensitive,
- mentions: status.mentions.map((mention) =>
- mention.instance
- ? `${mention.instance.domain}:${mention.id}`
- : mention.id,
- ),
- quotes: quoteReference,
- replies_to: replyReference,
- subject: status.spoilerText,
- // TODO: Refactor as part of groups
- group: status.visibility === "public" ? "public" : "followers",
- extensions: {
- "pub.versia:custom_emojis": {
- emojis: status.emojis.map((emoji) =>
- new Emoji(emoji).toVersia(),
- ),
- },
- // TODO: Add polls and reactions
- },
- });
+ status.author.instance?.domain ?? config.http.base_url.hostname,
+ );
}
public toVersiaShare(): VersiaEntities.Share {
@@ -1373,25 +1360,31 @@ export class Note extends BaseInterface {
throw new Error("Cannot share a non-reblogged note");
}
- return new VersiaEntities.Share({
- type: "pub.versia:share/Share",
- author: this.author.id,
- id: this.id,
- created_at: new Date().toISOString(),
- shared: this.data.reblog.author.instance
- ? `${this.data.reblog.author.instance.domain}:${this.data.reblog.id}`
- : this.data.reblog.id,
- });
+ return new VersiaEntities.Share(
+ {
+ type: "pub.versia:share/Share",
+ author: this.author.id,
+ id: this.id,
+ created_at: new Date().toISOString(),
+ shared: this.data.reblog.author.instance
+ ? `${this.data.reblog.author.instance.domain}:${this.data.reblog.id}`
+ : this.data.reblog.id,
+ },
+ this.data.author.instance?.domain ?? config.http.base_url.hostname,
+ );
}
public toVersiaUnshare(): VersiaEntities.Delete {
- return new VersiaEntities.Delete({
- type: "Delete",
- created_at: new Date().toISOString(),
- author: this.author.id,
- deleted_type: "pub.versia:share/Share",
- deleted: this.id,
- });
+ return new VersiaEntities.Delete(
+ {
+ type: "Delete",
+ created_at: new Date().toISOString(),
+ author: this.author.id,
+ deleted_type: "pub.versia:share/Share",
+ deleted: this.id,
+ },
+ this.data.author.instance?.domain ?? config.http.base_url.hostname,
+ );
}
/**
diff --git a/packages/kit/db/reaction.ts b/packages/kit/db/reaction.ts
index 88310b4f..8ede1b4e 100644
--- a/packages/kit/db/reaction.ts
+++ b/packages/kit/db/reaction.ts
@@ -26,7 +26,9 @@ import type { User } from "./user.ts";
type ReactionType = InferSelectModel & {
emoji: typeof Emoji.$type | null;
- author: InferSelectModel;
+ author: InferSelectModel & {
+ instance: InferSelectModel | null;
+ };
note: InferSelectModel & {
author: InferSelectModel & {
instance: InferSelectModel | null;
@@ -72,7 +74,11 @@ export class Reaction extends BaseInterface {
media: true,
},
},
- author: true,
+ author: {
+ with: {
+ instance: true,
+ },
+ },
note: {
with: {
author: {
@@ -112,7 +118,11 @@ export class Reaction extends BaseInterface {
media: true,
},
},
- author: true,
+ author: {
+ with: {
+ instance: true,
+ },
+ },
note: {
with: {
author: {
@@ -245,37 +255,43 @@ export class Reaction extends BaseInterface {
noteReference = `${this.data.note.author.instance.domain}:${this.data.note.remoteId}`;
}
- return new VersiaEntities.Reaction({
- type: "pub.versia:reactions/Reaction",
- author: this.data.author.id,
- created_at: this.data.createdAt.toISOString(),
- id: this.id,
- object: noteReference,
- content: this.hasCustomEmoji()
- ? `:${this.data.emoji?.shortcode}:`
- : this.data.emojiText || "",
- extensions: this.hasCustomEmoji()
- ? {
- "pub.versia:custom_emojis": {
- emojis: [
- new Emoji(
- this.data.emoji as typeof Emoji.$type,
- ).toVersia(),
- ],
- },
- }
- : undefined,
- });
+ return new VersiaEntities.Reaction(
+ {
+ type: "pub.versia:reactions/Reaction",
+ author: this.data.author.id,
+ created_at: this.data.createdAt.toISOString(),
+ id: this.id,
+ object: noteReference,
+ content: this.hasCustomEmoji()
+ ? `:${this.data.emoji?.shortcode}:`
+ : this.data.emojiText || "",
+ extensions: this.hasCustomEmoji()
+ ? {
+ "pub.versia:custom_emojis": {
+ emojis: [
+ new Emoji(
+ this.data.emoji as typeof Emoji.$type,
+ ).toVersia(),
+ ],
+ },
+ }
+ : undefined,
+ },
+ this.data.author.instance?.domain ?? config.http.base_url.hostname,
+ );
}
public toVersiaUnreact(): VersiaEntities.Delete {
- return new VersiaEntities.Delete({
- type: "Delete",
- created_at: new Date().toISOString(),
- author: this.data.authorId,
- deleted_type: "pub.versia:reactions/Reaction",
- deleted: this.id,
- });
+ return new VersiaEntities.Delete(
+ {
+ type: "Delete",
+ created_at: new Date().toISOString(),
+ author: this.data.authorId,
+ deleted_type: "pub.versia:reactions/Reaction",
+ deleted: this.id,
+ },
+ this.data.author.instance?.domain ?? config.http.base_url.hostname,
+ );
}
public static async fromVersia(
diff --git a/packages/kit/db/user.ts b/packages/kit/db/user.ts
index 1d97e42c..9ab413da 100644
--- a/packages/kit/db/user.ts
+++ b/packages/kit/db/user.ts
@@ -224,7 +224,10 @@ export class User extends BaseInterface {
public get reference(): VersiaEntities.Reference {
if (this.local) {
- return new VersiaEntities.Reference(this.id);
+ return new VersiaEntities.Reference(
+ this.id,
+ config.http.base_url.hostname,
+ );
}
return new VersiaEntities.Reference(
@@ -330,14 +333,17 @@ export class User extends BaseInterface {
}
private unfollowToVersia(followee: User): VersiaEntities.Unfollow {
- return new VersiaEntities.Unfollow({
- type: "Unfollow",
- author: this.id,
- created_at: new Date().toISOString(),
- followee: followee.data.instance
- ? `${followee.data.instance.domain}:${followee.id}`
- : followee.id,
- });
+ return new VersiaEntities.Unfollow(
+ {
+ type: "Unfollow",
+ author: this.id,
+ created_at: new Date().toISOString(),
+ followee: followee.data.instance
+ ? `${followee.data.instance.domain}:${followee.id}`
+ : followee.id,
+ },
+ this.data.instance?.domain ?? config.http.base_url.hostname,
+ );
}
public async acceptFollowRequest(follower: User): Promise {
@@ -352,14 +358,17 @@ export class User extends BaseInterface {
await follower.recalculateFollowerCount();
await this.recalculateFollowingCount();
- const entity = new VersiaEntities.FollowAccept({
- type: "FollowAccept",
- author: this.id,
- created_at: new Date().toISOString(),
- follower: follower.data.instance
- ? `${follower.data.instance.domain}:${follower.id}`
- : follower.id,
- });
+ const entity = new VersiaEntities.FollowAccept(
+ {
+ type: "FollowAccept",
+ author: this.id,
+ created_at: new Date().toISOString(),
+ follower: follower.data.instance
+ ? `${follower.data.instance.domain}:${follower.id}`
+ : follower.id,
+ },
+ this.data.instance?.domain ?? config.http.base_url.hostname,
+ );
await deliveryQueue.add(DeliveryJobType.FederateEntity, {
entity: entity.toJSON(),
@@ -377,14 +386,17 @@ export class User extends BaseInterface {
throw new Error("Followee must be a local user");
}
- const entity = new VersiaEntities.FollowReject({
- type: "FollowReject",
- author: this.id,
- created_at: new Date().toISOString(),
- follower: follower.data.instance
- ? `${follower.data.instance.domain}:${follower.id}`
- : follower.id,
- });
+ const entity = new VersiaEntities.FollowReject(
+ {
+ type: "FollowReject",
+ author: this.id,
+ created_at: new Date().toISOString(),
+ follower: follower.data.instance
+ ? `${follower.data.instance.domain}:${follower.id}`
+ : follower.id,
+ },
+ this.data.instance?.domain ?? config.http.base_url.hostname,
+ );
await deliveryQueue.add(DeliveryJobType.FederateEntity, {
entity: entity.toJSON(),
@@ -686,12 +698,6 @@ export class User extends BaseInterface {
instance?: Instance,
): Promise {
if (versiaUser instanceof VersiaEntities.Reference) {
- if (!versiaUser.domain) {
- throw new Error(
- "Cannot fetch Versia user from reference without domain",
- );
- }
-
const user = await Instance.federationRequester.fetchEntity(
versiaUser,
VersiaEntities.User,
@@ -802,13 +808,9 @@ export class User extends BaseInterface {
public static async resolve(
reference: VersiaEntities.Reference,
- defaultInstance?: Instance,
): Promise {
// Check if user not already in database
- if (
- !(reference.domain || defaultInstance) ||
- reference.domain === config.http.base_url.hostname
- ) {
+ if (reference.domain === config.http.base_url.hostname) {
const user = await User.fromId(reference.id);
if (!user) {
@@ -820,9 +822,7 @@ export class User extends BaseInterface {
return user;
}
- const instance = reference.domain
- ? await Instance.resolve(reference.domain)
- : (defaultInstance as Instance);
+ const instance = await Instance.resolve(reference.domain);
const foundUser = await User.fromSql(
and(
@@ -836,12 +836,7 @@ export class User extends BaseInterface {
}
return User.fromVersia(
- reference.domain
- ? reference
- : new VersiaEntities.Reference(
- reference.id,
- instance.data.domain,
- ),
+ new VersiaEntities.Reference(reference.id, instance.data.domain),
);
}
@@ -1088,39 +1083,42 @@ export class User extends BaseInterface {
const user = this.data;
- return new VersiaEntities.User({
- id: user.id,
- type: "User",
- bio: {
- "text/html": {
- content: user.note,
- remote: false,
+ return new VersiaEntities.User(
+ {
+ id: user.id,
+ type: "User",
+ bio: {
+ "text/html": {
+ content: user.note,
+ remote: false,
+ },
+ "text/plain": {
+ content: htmlToText(user.note),
+ remote: false,
+ },
},
- "text/plain": {
- content: htmlToText(user.note),
- remote: false,
+ created_at: user.createdAt.toISOString(),
+ indexable: this.data.isIndexable,
+ username: user.username,
+ manually_approves_followers: this.data.isLocked,
+ avatar: this.avatar?.toVersia().data as z.infer<
+ typeof ImageContentFormatSchema
+ >,
+ header: this.header?.toVersia().data as z.infer<
+ typeof ImageContentFormatSchema
+ >,
+ display_name: user.displayName,
+ fields: user.fields,
+ extensions: {
+ "pub.versia:custom_emojis": {
+ emojis: user.emojis.map((emoji) =>
+ new Emoji(emoji).toVersia(),
+ ),
+ },
},
},
- created_at: user.createdAt.toISOString(),
- indexable: this.data.isIndexable,
- username: user.username,
- manually_approves_followers: this.data.isLocked,
- avatar: this.avatar?.toVersia().data as z.infer<
- typeof ImageContentFormatSchema
- >,
- header: this.header?.toVersia().data as z.infer<
- typeof ImageContentFormatSchema
- >,
- display_name: user.displayName,
- fields: user.fields,
- extensions: {
- "pub.versia:custom_emojis": {
- emojis: user.emojis.map((emoji) =>
- new Emoji(emoji).toVersia(),
- ),
- },
- },
- });
+ this.data.instance?.domain ?? config.http.base_url.hostname,
+ );
}
public toMention(): z.infer {
diff --git a/packages/kit/inbox-processor.ts b/packages/kit/inbox-processor.ts
index 37a24f33..9e097c2c 100644
--- a/packages/kit/inbox-processor.ts
+++ b/packages/kit/inbox-processor.ts
@@ -185,33 +185,31 @@ export class InboxProcessor {
// TODO: Rip out bridge code so this is never null
const instance = this.sender?.instance as Instance;
- await new EntitySorter(this.body)
+ await new EntitySorter(this.body, instance.data.domain)
.on(VersiaEntities.Note, (n) =>
InboxProcessor.processNote(n, instance),
)
.on(VersiaEntities.Follow, (f) =>
- InboxProcessor.processFollowRequest(f, instance),
+ InboxProcessor.processFollowRequest(f),
)
.on(VersiaEntities.FollowAccept, (f) =>
- InboxProcessor.processFollowAccept(f, instance),
+ InboxProcessor.processFollowAccept(f),
)
.on(VersiaEntities.FollowReject, (f) =>
- InboxProcessor.processFollowReject(f, instance),
+ InboxProcessor.processFollowReject(f),
)
.on(VersiaEntities.Like, (l) =>
- InboxProcessor.processLikeRequest(l, instance),
+ InboxProcessor.processLikeRequest(l),
)
.on(VersiaEntities.Delete, (d) =>
- InboxProcessor.processDelete(d, instance),
+ InboxProcessor.processDelete(d),
)
.on(VersiaEntities.User, (u) =>
InboxProcessor.processUser(u, instance),
)
- .on(VersiaEntities.Share, (s) =>
- InboxProcessor.processShare(s, instance),
- )
+ .on(VersiaEntities.Share, (s) => InboxProcessor.processShare(s))
.on(VersiaEntities.Reaction, (r) =>
- InboxProcessor.processReaction(r, instance),
+ InboxProcessor.processReaction(r),
)
.sort(() => {
throw new ApiError(400, "Unknown entity type");
@@ -229,10 +227,9 @@ export class InboxProcessor {
*/
private static async processReaction(
reaction: VersiaEntities.Reaction,
- sender: Instance,
): Promise {
- const author = await User.resolve(reaction.author, sender);
- const note = await Note.resolve(reaction.object, sender);
+ const author = await User.resolve(reaction.author);
+ const note = await Note.resolve(reaction.object);
if (!author) {
throw new ApiError(404, "Author not found");
@@ -322,10 +319,9 @@ export class InboxProcessor {
*/
private static async processFollowRequest(
follow: VersiaEntities.Follow,
- sender: Instance,
): Promise {
- const author = await User.resolve(follow.author, sender);
- const followee = await User.resolve(follow.followee, sender);
+ const author = await User.resolve(follow.author);
+ const followee = await User.resolve(follow.followee);
if (!author) {
throw new ApiError(404, "Author not found");
@@ -371,10 +367,9 @@ export class InboxProcessor {
*/
private static async processFollowAccept(
followAccept: VersiaEntities.FollowAccept,
- sender: Instance,
): Promise {
- const author = await User.resolve(followAccept.author, sender);
- const follower = await User.resolve(followAccept.follower, sender);
+ const author = await User.resolve(followAccept.author);
+ const follower = await User.resolve(followAccept.follower);
if (!author) {
throw new ApiError(404, "Author not found");
@@ -407,10 +402,9 @@ export class InboxProcessor {
*/
private static async processFollowReject(
followReject: VersiaEntities.FollowReject,
- sender: Instance,
): Promise {
- const author = await User.resolve(followReject.author, sender);
- const follower = await User.resolve(followReject.follower, sender);
+ const author = await User.resolve(followReject.author);
+ const follower = await User.resolve(followReject.follower);
if (!author) {
throw new ApiError(404, "Author not found");
@@ -443,10 +437,9 @@ export class InboxProcessor {
*/
private static async processShare(
share: VersiaEntities.Share,
- sender: Instance,
): Promise {
- const author = await User.resolve(share.author, sender);
- const sharedNote = await Note.resolve(share.shared, sender);
+ const author = await User.resolve(share.author);
+ const sharedNote = await Note.resolve(share.shared);
if (!author) {
throw new ApiError(404, "Author not found");
@@ -467,11 +460,10 @@ export class InboxProcessor {
*/ // JS doesn't allow the use of `delete` as a variable name
public static async processDelete(
delete_: VersiaEntities.Delete,
- sender: Instance,
): Promise {
const toDelete = delete_.deleted;
- const author = await User.resolve(delete_.author, sender);
+ const author = await User.resolve(delete_.author);
switch (delete_.data.deleted_type) {
case "Note": {
@@ -491,7 +483,7 @@ export class InboxProcessor {
return;
}
case "User": {
- const userToDelete = await User.resolve(toDelete, sender);
+ const userToDelete = await User.resolve(toDelete);
if (!userToDelete) {
throw new ApiError(404, "User to delete not found");
@@ -586,10 +578,9 @@ export class InboxProcessor {
*/
private static async processLikeRequest(
like: VersiaEntities.Like,
- sender: Instance,
): Promise {
- const author = await User.resolve(like.author, sender);
- const likedNote = await Note.resolve(like.liked, sender);
+ const author = await User.resolve(like.author);
+ const likedNote = await Note.resolve(like.liked);
if (!author) {
throw new ApiError(404, "Author not found");
diff --git a/packages/kit/queues/delivery/worker.ts b/packages/kit/queues/delivery/worker.ts
index b56d475d..964eed29 100644
--- a/packages/kit/queues/delivery/worker.ts
+++ b/packages/kit/queues/delivery/worker.ts
@@ -62,7 +62,10 @@ export const getDeliveryWorker = (): Worker<
}
await sender.federateToUser(
- await entityCtor.fromJSON(entity),
+ await entityCtor.fromJSON(
+ entity,
+ config.http.base_url.hostname,
+ ),
recipient,
);
diff --git a/packages/sdk/entities/collection.ts b/packages/sdk/entities/collection.ts
index a1093b29..a3d79b60 100644
--- a/packages/sdk/entities/collection.ts
+++ b/packages/sdk/entities/collection.ts
@@ -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) {
- super(data);
+ public constructor(
+ public override data: z.infer,
+ instanceDomain: string,
+ ) {
+ super(data, instanceDomain);
}
- public static override fromJSON(json: JSONObject): Promise {
- return CollectionSchema.parseAsync(json).then((u) => new Collection(u));
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
+ return CollectionSchema.parseAsync(json).then(
+ (u) => new Collection(u, instanceDomain),
+ );
}
}
export class URICollection extends Entity {
public constructor(
public override data: z.infer,
+ instanceDomain: string,
) {
- super(data);
+ super(data, instanceDomain);
}
- public static override fromJSON(json: JSONObject): Promise {
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
return URICollectionSchema.parseAsync(json).then(
- (u) => new URICollection(u),
+ (u) => new URICollection(u, instanceDomain),
);
}
}
diff --git a/packages/sdk/entities/delete.ts b/packages/sdk/entities/delete.ts
index 7a16b5fa..3d572cf0 100644
--- a/packages/sdk/entities/delete.ts
+++ b/packages/sdk/entities/delete.ts
@@ -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) {
- super(data);
+ public constructor(
+ public override data: z.infer,
+ 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 {
- return DeleteSchema.parseAsync(json).then((u) => new Delete(u));
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
+ return DeleteSchema.parseAsync(json).then(
+ (u) => new Delete(u, instanceDomain),
+ );
}
}
diff --git a/packages/sdk/entities/entity.ts b/packages/sdk/entities/entity.ts
index a21beef6..2cd71149 100644
--- a/packages/sdk/entities/entity.ts
+++ b/packages/sdk/entities/entity.ts
@@ -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 {
- return EntitySchema.parseAsync(json).then((u) => new Entity(u));
+ public static fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
+ 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}`;
}
}
diff --git a/packages/sdk/entities/extensions/likes.ts b/packages/sdk/entities/extensions/likes.ts
index 8615b3fc..18460fba 100644
--- a/packages/sdk/entities/extensions/likes.ts
+++ b/packages/sdk/entities/extensions/likes.ts
@@ -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) {
- super(data);
+ public constructor(
+ public override data: z.infer,
+ 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 {
- return LikeSchema.parseAsync(json).then((u) => new Like(u));
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
+ 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) {
- super(data);
+ public constructor(
+ public override data: z.infer,
+ 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 {
- return DislikeSchema.parseAsync(json).then((u) => new Dislike(u));
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
+ return DislikeSchema.parseAsync(json).then(
+ (u) => new Dislike(u, instanceDomain),
+ );
}
}
diff --git a/packages/sdk/entities/extensions/polls.ts b/packages/sdk/entities/extensions/polls.ts
index 4c508552..02aaf2c8 100644
--- a/packages/sdk/entities/extensions/polls.ts
+++ b/packages/sdk/entities/extensions/polls.ts
@@ -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) {
- super(data);
+ public constructor(
+ public override data: z.infer,
+ 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 {
- return VoteSchema.parseAsync(json).then((u) => new Vote(u));
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
+ return VoteSchema.parseAsync(json).then(
+ (u) => new Vote(u, instanceDomain),
+ );
}
}
diff --git a/packages/sdk/entities/extensions/reactions.ts b/packages/sdk/entities/extensions/reactions.ts
index 3299c248..e515c48c 100644
--- a/packages/sdk/entities/extensions/reactions.ts
+++ b/packages/sdk/entities/extensions/reactions.ts
@@ -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) {
- super(data);
+ public constructor(
+ public override data: z.infer,
+ 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 {
- return ReactionSchema.parseAsync(json).then((u) => new Reaction(u));
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
+ return ReactionSchema.parseAsync(json).then(
+ (u) => new Reaction(u, instanceDomain),
+ );
}
}
diff --git a/packages/sdk/entities/extensions/reports.ts b/packages/sdk/entities/extensions/reports.ts
index 31c2ad5e..0ca914cb 100644
--- a/packages/sdk/entities/extensions/reports.ts
+++ b/packages/sdk/entities/extensions/reports.ts
@@ -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) {
- super(data);
+ public constructor(
+ public override data: z.infer,
+ 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 {
- return ReportSchema.parseAsync(json).then((u) => new Report(u));
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
+ return ReportSchema.parseAsync(json).then(
+ (u) => new Report(u, instanceDomain),
+ );
}
}
diff --git a/packages/sdk/entities/extensions/share.ts b/packages/sdk/entities/extensions/share.ts
index ea2fb8ad..9846a06a 100644
--- a/packages/sdk/entities/extensions/share.ts
+++ b/packages/sdk/entities/extensions/share.ts
@@ -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) {
- super(data);
+ public constructor(
+ public override data: z.infer,
+ 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 {
- return ShareSchema.parseAsync(json).then((u) => new Share(u));
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
+ return ShareSchema.parseAsync(json).then(
+ (u) => new Share(u, instanceDomain),
+ );
}
}
diff --git a/packages/sdk/entities/follow.ts b/packages/sdk/entities/follow.ts
index 9ba9db5f..389c180b 100644
--- a/packages/sdk/entities/follow.ts
+++ b/packages/sdk/entities/follow.ts
@@ -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) {
- super(data);
+ public constructor(
+ public override data: z.infer,
+ 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 {
- return FollowSchema.parseAsync(json).then((u) => new Follow(u));
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
+ 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,
+ 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 {
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
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,
+ 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 {
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
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) {
- super(data);
+ public constructor(
+ public override data: z.infer,
+ 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 {
- return UnfollowSchema.parseAsync(json).then((u) => new Unfollow(u));
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
+ return UnfollowSchema.parseAsync(json).then(
+ (u) => new Unfollow(u, instanceDomain),
+ );
}
}
diff --git a/packages/sdk/entities/instancemetadata.ts b/packages/sdk/entities/instancemetadata.ts
index d95f44ab..4b535dc2 100644
--- a/packages/sdk/entities/instancemetadata.ts
+++ b/packages/sdk/entities/instancemetadata.ts
@@ -10,7 +10,7 @@ export class InstanceMetadata extends Entity {
public constructor(
public override data: z.infer,
) {
- super(data);
+ super(data, data.domain);
}
public get logo(): ImageContentFormat | undefined {
diff --git a/packages/sdk/entities/note.ts b/packages/sdk/entities/note.ts
index 3a84cb68..88d6d811 100644
--- a/packages/sdk/entities/note.ts
+++ b/packages/sdk/entities/note.ts
@@ -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) {
- super(data);
+ public constructor(
+ public override data: z.infer,
+ instanceDomain: string,
+ ) {
+ super(data, instanceDomain);
}
- public static override fromJSON(json: JSONObject): Promise {
- return NoteSchema.parseAsync(json).then((n) => new Note(n));
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
+ 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;
}
diff --git a/packages/sdk/entities/user.ts b/packages/sdk/entities/user.ts
index 0d744632..c83d88cc 100644
--- a/packages/sdk/entities/user.ts
+++ b/packages/sdk/entities/user.ts
@@ -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) {
- super(data);
+ public constructor(
+ public override data: z.infer,
+ instanceDomain: string,
+ ) {
+ super(data, instanceDomain);
}
- public static override fromJSON(json: JSONObject): Promise {
- return UserSchema.parseAsync(json).then((u) => new User(u));
+ public static override fromJSON(
+ json: JSONObject,
+ instanceDomain: string,
+ ): Promise {
+ return UserSchema.parseAsync(json).then(
+ (u) => new User(u, instanceDomain),
+ );
}
public get avatar(): ImageContentFormat | undefined {
diff --git a/packages/sdk/http.ts b/packages/sdk/http.ts
index e87aef6a..dd40600b 100644
--- a/packages/sdk/http.ts
+++ b/packages/sdk/http.ts
@@ -75,7 +75,7 @@ export class FederationRequester {
);
}
- const entity = await entityType.fromJSON(jsonData);
+ const entity = await entityType.fromJSON(jsonData, url.hostname);
return entity as InstanceType;
}
@@ -150,7 +150,10 @@ export class FederationRequester {
entities.push(
...collection.data.items.map(
(item) =>
- collectionItemType.fromJSON(item) as InstanceType,
+ collectionItemType.fromJSON(
+ item,
+ reference.domain,
+ ) as InstanceType,
),
);
limit -= collection.data.items.length;
diff --git a/packages/sdk/inbox-processor.ts b/packages/sdk/inbox-processor.ts
index 1fee5da1..59cd74cf 100644
--- a/packages/sdk/inbox-processor.ts
+++ b/packages/sdk/inbox-processor.ts
@@ -19,7 +19,10 @@ type MaybePromise = T | Promise;
export class EntitySorter {
private readonly handlers: EntitySorterHandlers = new Map();
- public constructor(private readonly jsonData: JSONObject) {}
+ public constructor(
+ private readonly jsonData: JSONObject,
+ public instanceDomain: string,
+ ) {}
public on(
entity: T,
@@ -45,7 +48,7 @@ export class EntitySorter {
if (entity) {
await this.handlers.get(entity)?.(
- await entity.fromJSON(this.jsonData),
+ await entity.fromJSON(this.jsonData, this.instanceDomain),
);
} else {
await defaultHandler?.();