Skip to content

Commit 5114df4

Browse files
committed
feat(federation): 👽 Update to Versia 0.5
1 parent afec384 commit 5114df4

8 files changed

Lines changed: 220 additions & 31 deletions

File tree

federation/http.ts

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import type {
44
Follow,
55
FollowAccept,
66
FollowReject,
7-
Group,
7+
GroupExtensionSubscribe,
8+
GroupExtensionSubscribeAccept,
9+
GroupExtensionSubscribeReject,
10+
GroupExtensionUnsubscribe,
811
InstanceMetadata,
912
LikeExtension,
1013
Note,
@@ -28,7 +31,18 @@ type ParserCallbacks<T> = {
2831
"pub.versia:likes/Dislike": (dislike: DislikeExtension) => MaybePromise<T>;
2932
delete: (undo: Delete) => MaybePromise<T>;
3033
instanceMetadata: (instanceMetadata: InstanceMetadata) => MaybePromise<T>;
31-
group: (group: Group) => MaybePromise<T>;
34+
"pub.versia:groups/Subscribe": (
35+
groupSubscribe: GroupExtensionSubscribe,
36+
) => MaybePromise<T>;
37+
"pub.versia:groups/SubscribeAccept": (
38+
groupSubscribeAccept: GroupExtensionSubscribeAccept,
39+
) => MaybePromise<T>;
40+
"pub.versia:groups/SubscribeReject": (
41+
groupSubscribeReject: GroupExtensionSubscribeReject,
42+
) => MaybePromise<T>;
43+
"pub.versia:groups/Unsubscribe": (
44+
groupUnsubscribe: GroupExtensionUnsubscribe,
45+
) => MaybePromise<T>;
3246
"pub.versia:reactions/Reaction": (
3347
reaction: ReactionExtension,
3448
) => MaybePromise<T>;
@@ -182,11 +196,52 @@ export class RequestParserHandler {
182196

183197
break;
184198
}
185-
case "Group": {
186-
const group = await this.validator.Group(this.body);
199+
case "pub.versia:groups/Subscribe": {
200+
const groupSubscribe = await this.validator.GroupSubscribe(
201+
this.body,
202+
);
203+
204+
if (callbacks["pub.versia:groups/Subscribe"]) {
205+
return await callbacks["pub.versia:groups/Subscribe"](
206+
groupSubscribe,
207+
);
208+
}
209+
210+
break;
211+
}
212+
case "pub.versia:groups/SubscribeAccept": {
213+
const groupSubscribeAccept =
214+
await this.validator.GroupSubscribeAccept(this.body);
187215

188-
if (callbacks.group) {
189-
return await callbacks.group(group);
216+
if (callbacks["pub.versia:groups/SubscribeAccept"]) {
217+
return await callbacks["pub.versia:groups/SubscribeAccept"](
218+
groupSubscribeAccept,
219+
);
220+
}
221+
222+
break;
223+
}
224+
case "pub.versia:groups/SubscribeReject": {
225+
const groupSubscribeReject =
226+
await this.validator.GroupSubscribeReject(this.body);
227+
228+
if (callbacks["pub.versia:groups/SubscribeReject"]) {
229+
return await callbacks["pub.versia:groups/SubscribeReject"](
230+
groupSubscribeReject,
231+
);
232+
}
233+
234+
break;
235+
}
236+
case "pub.versia:groups/Unsubscribe": {
237+
const groupUnsubscribe = await this.validator.GroupUnsubscribe(
238+
this.body,
239+
);
240+
241+
if (callbacks["pub.versia:groups/Unsubscribe"]) {
242+
return await callbacks["pub.versia:groups/Unsubscribe"](
243+
groupUnsubscribe,
244+
);
190245
}
191246

192247
break;

federation/schemas.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export {
1111
FollowAcceptSchema as FollowAccept,
1212
FollowRejectSchema as FollowReject,
1313
FollowSchema as Follow,
14-
GroupSchema as Group,
14+
URICollectionSchema as URICollection,
1515
InstanceMetadataSchema as InstanceMetadata,
1616
NoteSchema as Note,
1717
UnfollowSchema as Unfollow,
@@ -20,6 +20,13 @@ export {
2020
export { ContentFormatSchema as ContentFormat } from "./schemas/content_format.ts";
2121
export { ExtensionPropertySchema as EntityExtensionProperty } from "./schemas/extensions.ts";
2222
export { CustomEmojiExtensionSchema as CustomEmojiExtension } from "./schemas/extensions/custom_emojis.ts";
23+
export {
24+
GroupSchema as GroupExtension,
25+
GroupSubscribeSchema as GroupExtensionSubscribe,
26+
GroupSubscribeAcceptSchema as GroupExtensionSubscribeAccept,
27+
GroupSubscribeRejectSchema as GroupExtensionSubscribeReject,
28+
GroupUnsubscribeSchema as GroupExtensionUnsubscribe,
29+
} from "./schemas/extensions/groups.ts";
2330
export {
2431
DislikeSchema as DislikeExtension,
2532
LikeSchema as LikeExtension,

federation/schemas/base.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { extensionRegex, isISOString, semverRegex } from "./regex.ts";
1010

1111
export const EntitySchema = z
1212
.object({
13+
// biome-ignore lint/style/useNamingConvention:
14+
$schema: z.string().url().optional().nullable(),
1315
id: z.string().max(512),
1416
created_at: z
1517
.string()
@@ -37,6 +39,17 @@ export const NoteSchema = EntitySchema.extend({
3739
.optional()
3840
.nullable(),
3941
content: TextOnlyContentFormatSchema.optional().nullable(),
42+
collections: z.object({
43+
replies: z.string().url(),
44+
quotes: z.string().url(),
45+
"pub.versia:reactions/Reactions": z
46+
.string()
47+
.url()
48+
.optional()
49+
.nullable(),
50+
"pub.versia:likes/Likes": z.string().url().optional().nullable(),
51+
"pub.versia:likes/Dislikes": z.string().url().optional().nullable(),
52+
}),
4053
device: z
4154
.object({
4255
name: z.string(),
@@ -72,13 +85,6 @@ export const NoteSchema = EntitySchema.extend({
7285
replies_to: z.string().url().optional().nullable(),
7386
subject: z.string().optional().nullable(),
7487
extensions: ExtensionPropertySchema.extend({
75-
"pub.versia:reactions": z
76-
.object({
77-
reactions: z.string().url(),
78-
})
79-
.strict()
80-
.optional()
81-
.nullable(),
8288
"pub.versia:polls": z
8389
.object({
8490
options: z.array(TextOnlyContentFormatSchema),
@@ -119,6 +125,10 @@ export const CollectionSchema = z.object({
119125
items: z.array(z.any()),
120126
});
121127

128+
export const URICollectionSchema = CollectionSchema.extend({
129+
items: z.array(z.string().url()),
130+
});
131+
122132
export const PublicKeyDataSchema = z
123133
.object({
124134
key: z.string().min(1),
@@ -147,8 +157,8 @@ export const UserSchema = EntitySchema.extend({
147157
.string()
148158
.min(1)
149159
.regex(
150-
/^[a-z0-9_-]+$/,
151-
"must be lowercase, alphanumeric, and may contain _ or -",
160+
/^[a-zA-Z0-9_-]+$/,
161+
"must be alphanumeric, and may contain _ or -",
152162
),
153163
header: ImageOnlyContentFormatSchema.optional().nullable(),
154164
public_key: PublicKeyDataSchema,
@@ -208,14 +218,6 @@ export const UnfollowSchema = EntitySchema.extend({
208218
followee: z.string().url(),
209219
});
210220

211-
export const GroupSchema = EntitySchema.extend({
212-
type: z.literal("Group"),
213-
name: TextOnlyContentFormatSchema.optional().nullable(),
214-
description: TextOnlyContentFormatSchema.optional().nullable(),
215-
members: z.string().url(),
216-
notes: z.string().url().optional().nullable(),
217-
});
218-
219221
export const InstanceMetadataSchema = EntitySchema.extend({
220222
type: z.literal("InstanceMetadata"),
221223
id: z.null().optional(),
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { z } from "zod";
2+
import { EntitySchema } from "../base.ts";
3+
import { TextOnlyContentFormatSchema } from "../content_format.ts";
4+
5+
export const GroupSchema = EntitySchema.extend({
6+
type: z.literal("pub.versia:groups/Group"),
7+
name: TextOnlyContentFormatSchema.optional().nullable(),
8+
description: TextOnlyContentFormatSchema.optional().nullable(),
9+
open: z.boolean().optional().nullable(),
10+
members: z.string().url(),
11+
notes: z.string().url().optional().nullable(),
12+
});
13+
14+
export const GroupSubscribeSchema = EntitySchema.extend({
15+
type: z.literal("pub.versia:groups/Subscribe"),
16+
uri: z.null().optional(),
17+
subscriber: z.string().url(),
18+
group: z.string().url(),
19+
});
20+
21+
export const GroupUnsubscribeSchema = EntitySchema.extend({
22+
type: z.literal("pub.versia:groups/Unsubscribe"),
23+
uri: z.null().optional(),
24+
subscriber: z.string().url(),
25+
group: z.string().url(),
26+
});
27+
28+
export const GroupSubscribeAcceptSchema = EntitySchema.extend({
29+
type: z.literal("pub.versia:groups/SubscribeAccept"),
30+
uri: z.null().optional(),
31+
subscriber: z.string().url(),
32+
group: z.string().url(),
33+
});
34+
35+
export const GroupSubscribeRejectSchema = EntitySchema.extend({
36+
type: z.literal("pub.versia:groups/SubscribeReject"),
37+
uri: z.null().optional(),
38+
subscriber: z.string().url(),
39+
group: z.string().url(),
40+
});

federation/schemas/extensions/vanity.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
AudioOnlyContentFormatSchema,
1111
ImageOnlyContentFormatSchema,
1212
} from "../content_format.ts";
13-
import { isISOString } from "../regex.ts";
13+
import { ianaTimezoneRegex, isISOString } from "../regex.ts";
1414

1515
/**
1616
* @description Vanity extension entity
@@ -103,5 +103,10 @@ export const VanityExtensionSchema = z
103103
.nullable(),
104104
location: z.string().optional().nullable(),
105105
aliases: z.array(z.string().url()).optional().nullable(),
106+
timezone: z
107+
.string()
108+
.regex(ianaTimezoneRegex, "must be a valid IANA timezone")
109+
.optional()
110+
.nullable(),
106111
})
107112
.strict();

federation/schemas/regex.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,5 @@ export const isISOString = (val: string | Date) => {
6666
const d = new Date(val);
6767
return !Number.isNaN(d.valueOf());
6868
};
69+
70+
export const ianaTimezoneRegex = /^(?:[A-Za-z]+(?:\/[A-Za-z_]+)+|UTC)$/;

federation/types.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,22 @@ import type {
1111
FollowAcceptSchema,
1212
FollowRejectSchema,
1313
FollowSchema,
14-
GroupSchema,
1514
InstanceMetadataSchema,
1615
NoteSchema,
16+
URICollectionSchema,
1717
UnfollowSchema,
1818
UserSchema,
1919
} from "./schemas/base.ts";
2020
import type { ContentFormatSchema } from "./schemas/content_format.ts";
2121
import type { ExtensionPropertySchema } from "./schemas/extensions.ts";
2222
import type { CustomEmojiExtensionSchema } from "./schemas/extensions/custom_emojis.ts";
23+
import type {
24+
GroupSchema,
25+
GroupSubscribeAcceptSchema,
26+
GroupSubscribeRejectSchema,
27+
GroupSubscribeSchema,
28+
GroupUnsubscribeSchema,
29+
} from "./schemas/extensions/groups.ts";
2330
import type { DislikeSchema, LikeSchema } from "./schemas/extensions/likes.ts";
2431
import type { VoteSchema } from "./schemas/extensions/polls.ts";
2532
import type { ReactionSchema } from "./schemas/extensions/reactions.ts";
@@ -33,6 +40,7 @@ type InferType<T extends AnyZod> = z.infer<T>;
3340

3441
export type Note = InferType<typeof NoteSchema>;
3542
export type Collection = InferType<typeof CollectionSchema>;
43+
export type URICollection = InferType<typeof URICollectionSchema>;
3644
export type EntityExtensionProperty = InferType<typeof ExtensionPropertySchema>;
3745
export type VanityExtension = InferType<typeof VanityExtensionSchema>;
3846
export type User = InferType<typeof UserSchema>;
@@ -43,9 +51,19 @@ export type ContentFormat = InferType<typeof ContentFormatSchema>;
4351
export type CustomEmojiExtension = InferType<typeof CustomEmojiExtensionSchema>;
4452
export type Entity = InferType<typeof EntitySchema>;
4553
export type Delete = InferType<typeof DeleteSchema>;
46-
export type Group = InferType<typeof GroupSchema>;
4754
export type InstanceMetadata = InferType<typeof InstanceMetadataSchema>;
4855
export type Unfollow = InferType<typeof UnfollowSchema>;
56+
export type GroupExtension = InferType<typeof GroupSchema>;
57+
export type GroupExtensionSubscribe = InferType<typeof GroupSubscribeSchema>;
58+
export type GroupExtensionSubscribeAccept = InferType<
59+
typeof GroupSubscribeAcceptSchema
60+
>;
61+
export type GroupExtensionSubscribeReject = InferType<
62+
typeof GroupSubscribeRejectSchema
63+
>;
64+
export type GroupExtensionUnsubscribe = InferType<
65+
typeof GroupUnsubscribeSchema
66+
>;
4967
export type LikeExtension = InferType<typeof LikeSchema>;
5068
export type DislikeExtension = InferType<typeof DislikeSchema>;
5169
export type PollVoteExtension = InferType<typeof VoteSchema>;

0 commit comments

Comments
 (0)