mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 05:49:16 +01:00
refactor: ⬆️ Upgrade to Zod v4 and hono-openapi 0.5.0
This commit is contained in:
parent
add2429606
commit
24d4150da4
209 changed files with 1331 additions and 1622 deletions
|
|
@ -1,11 +1,11 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Account } from "./account.ts";
|
||||
import { Appeal } from "./appeal.ts";
|
||||
import { Id } from "./common.ts";
|
||||
|
||||
export const AccountWarning = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "The ID of the account warning in the database.",
|
||||
example: "0968680e-fd64-4525-b818-6e1c46fbdb28",
|
||||
}),
|
||||
|
|
@ -19,40 +19,40 @@ export const AccountWarning = z
|
|||
"silence",
|
||||
"suspend",
|
||||
])
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Action taken against the account. 'none' = No action was taken, this is a simple warning; 'disable' = The account has been disabled; 'mark_statuses_as_sensitive' = Specific posts from the target account have been marked as sensitive; 'delete_statuses' = Specific statuses from the target account have been deleted; 'sensitive' = All posts from the target account are marked as sensitive; 'silence' = The target account has been limited; 'suspend' = The target account has been suspended.",
|
||||
example: "none",
|
||||
}),
|
||||
text: z.string().openapi({
|
||||
text: z.string().meta({
|
||||
description: "Message from the moderator to the target account.",
|
||||
example: "Please adhere to our community guidelines.",
|
||||
}),
|
||||
status_ids: z
|
||||
.array(Id)
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"List of status IDs that are relevant to the warning. When action is mark_statuses_as_sensitive or delete_statuses, those are the affected statuses.",
|
||||
example: ["5ee59275-c308-4173-bb1f-58646204579b"],
|
||||
}),
|
||||
target_account: Account.openapi({
|
||||
target_account: Account.meta({
|
||||
description:
|
||||
"Account against which a moderation decision has been taken.",
|
||||
}),
|
||||
appeal: Appeal.nullable().openapi({
|
||||
appeal: Appeal.nullable().meta({
|
||||
description: "Appeal submitted by the target account, if any.",
|
||||
example: null,
|
||||
}),
|
||||
created_at: z.string().datetime().openapi({
|
||||
created_at: z.iso.datetime().meta({
|
||||
description: "When the event took place.",
|
||||
example: "2025-01-04T14:11:00Z",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Moderation warning against a particular account.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/AccountWarning",
|
||||
},
|
||||
ref: "AccountWarning",
|
||||
id: "AccountWarning",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { userAddressRegex } from "../regex.ts";
|
||||
import { iso631, zBoolean } from "./common.ts";
|
||||
import { CustomEmoji } from "./emoji.ts";
|
||||
|
|
@ -10,7 +10,7 @@ export const Field = z
|
|||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The key of a given field’s key-value pair.",
|
||||
example: "Freak level",
|
||||
externalDocs: {
|
||||
|
|
@ -21,18 +21,17 @@ export const Field = z
|
|||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The value associated with the name key.",
|
||||
example: "<p>High</p>",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#value",
|
||||
},
|
||||
}),
|
||||
verified_at: z
|
||||
.string()
|
||||
verified_at: z.iso
|
||||
.datetime()
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Timestamp of when the server verified a URL value for a rel=“me” link.",
|
||||
example: null,
|
||||
|
|
@ -41,11 +40,11 @@ export const Field = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({ ref: "AccountField" });
|
||||
.meta({ id: "AccountField" });
|
||||
|
||||
export const Source = z
|
||||
.object({
|
||||
privacy: z.enum(["public", "unlisted", "private", "direct"]).openapi({
|
||||
privacy: z.enum(["public", "unlisted", "private", "direct"]).meta({
|
||||
description:
|
||||
"The default post privacy to be used for new statuses.",
|
||||
example: "unlisted",
|
||||
|
|
@ -53,7 +52,7 @@ export const Source = z
|
|||
url: "https://docs.joinmastodon.org/entities/Account/#source-privacy",
|
||||
},
|
||||
}),
|
||||
sensitive: zBoolean.openapi({
|
||||
sensitive: zBoolean.meta({
|
||||
description:
|
||||
"Whether new statuses should be marked sensitive by default.",
|
||||
example: false,
|
||||
|
|
@ -61,7 +60,7 @@ export const Source = z
|
|||
url: "https://docs.joinmastodon.org/entities/Account/#source-sensitive",
|
||||
},
|
||||
}),
|
||||
language: iso631.openapi({
|
||||
language: iso631.meta({
|
||||
description: "The default posting language for new statuses.",
|
||||
example: "en",
|
||||
externalDocs: {
|
||||
|
|
@ -73,7 +72,7 @@ export const Source = z
|
|||
.int()
|
||||
.nonnegative()
|
||||
.optional()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The number of pending follow requests.",
|
||||
example: 3,
|
||||
externalDocs: {
|
||||
|
|
@ -84,39 +83,35 @@ export const Source = z
|
|||
.string()
|
||||
.trim()
|
||||
.min(0)
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Profile bio, in plain-text instead of in HTML.",
|
||||
example: "ermmm what the meow meow",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#source-note",
|
||||
},
|
||||
}),
|
||||
fields: z.array(Field).openapi({
|
||||
fields: z.array(Field).meta({
|
||||
description: "Metadata about the account.",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"An extra attribute that contains source values to be used with API methods that verify credentials and update credentials.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#source",
|
||||
},
|
||||
ref: "AccountSource",
|
||||
id: "AccountSource",
|
||||
});
|
||||
|
||||
// Because Account has some recursive references, we need to define it like this
|
||||
const BaseAccount = z
|
||||
export const Account = z
|
||||
.object({
|
||||
id: z
|
||||
.string()
|
||||
.uuid()
|
||||
.openapi({
|
||||
description: "The account ID in the database.",
|
||||
example: "9e84842b-4db6-4a9b-969d-46ab408278da",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#id",
|
||||
},
|
||||
}),
|
||||
id: z.uuid().meta({
|
||||
description: "The account ID in the database.",
|
||||
example: "9e84842b-4db6-4a9b-969d-46ab408278da",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#id",
|
||||
},
|
||||
}),
|
||||
username: z
|
||||
.string()
|
||||
.min(3)
|
||||
|
|
@ -125,7 +120,7 @@ const BaseAccount = z
|
|||
/^[a-z0-9_-]+$/,
|
||||
"Username can only contain letters, numbers, underscores and hyphens",
|
||||
)
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"The username of the account, not including domain.",
|
||||
example: "lexi",
|
||||
|
|
@ -138,7 +133,7 @@ const BaseAccount = z
|
|||
.min(1)
|
||||
.trim()
|
||||
.regex(userAddressRegex, "Invalid user address")
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"The Webfinger account URI. Equal to username for local users, or username@domain for remote users.",
|
||||
example: "lexi@beta.versia.social",
|
||||
|
|
@ -146,21 +141,18 @@ const BaseAccount = z
|
|||
url: "https://docs.joinmastodon.org/entities/Account/#acct",
|
||||
},
|
||||
}),
|
||||
url: z
|
||||
.string()
|
||||
.url()
|
||||
.openapi({
|
||||
description: "The location of the user’s profile page.",
|
||||
example: "https://beta.versia.social/@lexi",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#url",
|
||||
},
|
||||
}),
|
||||
url: z.url().meta({
|
||||
description: "The location of the user’s profile page.",
|
||||
example: "https://beta.versia.social/@lexi",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#url",
|
||||
},
|
||||
}),
|
||||
display_name: z
|
||||
.string()
|
||||
.min(3)
|
||||
.trim()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The profile’s display name.",
|
||||
example: "Lexi :flower:",
|
||||
externalDocs: {
|
||||
|
|
@ -171,29 +163,26 @@ const BaseAccount = z
|
|||
.string()
|
||||
.min(0)
|
||||
.trim()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The profile’s bio or description.",
|
||||
example: "<p>ermmm what the meow meow</p>",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#note",
|
||||
},
|
||||
}),
|
||||
avatar: z
|
||||
.string()
|
||||
.url()
|
||||
.openapi({
|
||||
description:
|
||||
"An image icon that is shown next to statuses and in the profile.",
|
||||
example:
|
||||
"https://cdn.versia.social/avatars/cff9aea0-0000-43fe-8b5e-e7c7ea69a488/lexi.webp",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#avatar",
|
||||
},
|
||||
}),
|
||||
avatar: z.url().meta({
|
||||
description:
|
||||
"An image icon that is shown next to statuses and in the profile.",
|
||||
example:
|
||||
"https://cdn.versia.social/avatars/cff9aea0-0000-43fe-8b5e-e7c7ea69a488/lexi.webp",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#avatar",
|
||||
},
|
||||
}),
|
||||
avatar_static: z
|
||||
.string()
|
||||
.url()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"A static version of the avatar. Equal to avatar if its value is a static image; different if avatar is an animated GIF.",
|
||||
example:
|
||||
|
|
@ -202,31 +191,25 @@ const BaseAccount = z
|
|||
url: "https://docs.joinmastodon.org/entities/Account/#avatar_static",
|
||||
},
|
||||
}),
|
||||
header: z
|
||||
.string()
|
||||
.url()
|
||||
.openapi({
|
||||
description:
|
||||
"An image banner that is shown above the profile and in profile cards.",
|
||||
example:
|
||||
"https://cdn.versia.social/headers/a049f8e3-878c-4faa-ae4c-a6bcceddbd9d/femboy_2.webp",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#header",
|
||||
},
|
||||
}),
|
||||
header_static: z
|
||||
.string()
|
||||
.url()
|
||||
.openapi({
|
||||
description:
|
||||
"A static version of the header. Equal to header if its value is a static image; different if header is an animated GIF.",
|
||||
example:
|
||||
"https://cdn.versia.social/headers/a049f8e3-878c-4faa-ae4c-a6bcceddbd9d/femboy_2.webp",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#header_static",
|
||||
},
|
||||
}),
|
||||
locked: zBoolean.openapi({
|
||||
header: z.url().meta({
|
||||
description:
|
||||
"An image banner that is shown above the profile and in profile cards.",
|
||||
example:
|
||||
"https://cdn.versia.social/headers/a049f8e3-878c-4faa-ae4c-a6bcceddbd9d/femboy_2.webp",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#header",
|
||||
},
|
||||
}),
|
||||
header_static: z.url().meta({
|
||||
description:
|
||||
"A static version of the header. Equal to header if its value is a static image; different if header is an animated GIF.",
|
||||
example:
|
||||
"https://cdn.versia.social/headers/a049f8e3-878c-4faa-ae4c-a6bcceddbd9d/femboy_2.webp",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#header_static",
|
||||
},
|
||||
}),
|
||||
locked: zBoolean.meta({
|
||||
description:
|
||||
"Whether the account manually approves follow requests.",
|
||||
example: false,
|
||||
|
|
@ -234,21 +217,21 @@ const BaseAccount = z
|
|||
url: "https://docs.joinmastodon.org/entities/Account/#locked",
|
||||
},
|
||||
}),
|
||||
fields: z.array(Field).openapi({
|
||||
fields: z.array(Field).meta({
|
||||
description:
|
||||
"Additional metadata attached to a profile as name-value pairs.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#fields",
|
||||
},
|
||||
}),
|
||||
emojis: z.array(CustomEmoji).openapi({
|
||||
emojis: z.array(CustomEmoji).meta({
|
||||
description:
|
||||
"Custom emoji entities to be used when rendering the profile.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#emojis",
|
||||
},
|
||||
}),
|
||||
bot: zBoolean.openapi({
|
||||
bot: zBoolean.meta({
|
||||
description:
|
||||
"Indicates that the account may perform automated actions, may not be monitored, or identifies as a robot.",
|
||||
example: false,
|
||||
|
|
@ -256,14 +239,14 @@ const BaseAccount = z
|
|||
url: "https://docs.joinmastodon.org/entities/Account/#bot",
|
||||
},
|
||||
}),
|
||||
group: z.literal(false).openapi({
|
||||
group: z.literal(false).meta({
|
||||
description: "Indicates that the account represents a Group actor.",
|
||||
example: false,
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#group",
|
||||
},
|
||||
}),
|
||||
discoverable: zBoolean.nullable().openapi({
|
||||
discoverable: zBoolean.nullable().meta({
|
||||
description:
|
||||
"Whether the account has opted into discovery features such as the profile directory.",
|
||||
example: true,
|
||||
|
|
@ -274,7 +257,7 @@ const BaseAccount = z
|
|||
noindex: zBoolean
|
||||
.nullable()
|
||||
.optional()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Whether the local user has opted out of being indexed by search engines.",
|
||||
example: false,
|
||||
|
|
@ -282,7 +265,7 @@ const BaseAccount = z
|
|||
url: "https://docs.joinmastodon.org/entities/Account/#noindex",
|
||||
},
|
||||
}),
|
||||
suspended: zBoolean.optional().openapi({
|
||||
suspended: zBoolean.optional().meta({
|
||||
description:
|
||||
"An extra attribute returned only when an account is suspended.",
|
||||
example: false,
|
||||
|
|
@ -290,7 +273,7 @@ const BaseAccount = z
|
|||
url: "https://docs.joinmastodon.org/entities/Account/#suspended",
|
||||
},
|
||||
}),
|
||||
limited: zBoolean.optional().openapi({
|
||||
limited: zBoolean.optional().meta({
|
||||
description:
|
||||
"An extra attribute returned only when an account is silenced. If true, indicates that the account should be hidden behind a warning screen.",
|
||||
example: false,
|
||||
|
|
@ -298,20 +281,17 @@ const BaseAccount = z
|
|||
url: "https://docs.joinmastodon.org/entities/Account/#limited",
|
||||
},
|
||||
}),
|
||||
created_at: z
|
||||
.string()
|
||||
.datetime()
|
||||
.openapi({
|
||||
description: "When the account was created.",
|
||||
example: "2024-10-15T22:00:00.000Z",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#created_at",
|
||||
},
|
||||
}),
|
||||
created_at: z.iso.datetime().meta({
|
||||
description: "When the account was created.",
|
||||
example: "2024-10-15T22:00:00.000Z",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#created_at",
|
||||
},
|
||||
}),
|
||||
// TODO
|
||||
last_status_at: z
|
||||
.literal(null)
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "When the most recent status was posted.",
|
||||
example: null,
|
||||
externalDocs: {
|
||||
|
|
@ -323,7 +303,7 @@ const BaseAccount = z
|
|||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "How many statuses are attached to this account.",
|
||||
example: 42,
|
||||
externalDocs: {
|
||||
|
|
@ -334,7 +314,7 @@ const BaseAccount = z
|
|||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The reported followers of this profile.",
|
||||
example: 6,
|
||||
externalDocs: {
|
||||
|
|
@ -345,7 +325,7 @@ const BaseAccount = z
|
|||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The reported follows of this profile.",
|
||||
example: 23,
|
||||
externalDocs: {
|
||||
|
|
@ -353,7 +333,7 @@ const BaseAccount = z
|
|||
},
|
||||
}),
|
||||
/* Versia Server API extension */
|
||||
uri: z.string().url().openapi({
|
||||
uri: z.url().meta({
|
||||
description:
|
||||
"The location of the user's Versia profile page, as opposed to the local representation.",
|
||||
example:
|
||||
|
|
@ -365,26 +345,25 @@ const BaseAccount = z
|
|||
name: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
get moved() {
|
||||
return Account.nullable()
|
||||
.optional()
|
||||
.meta({
|
||||
description:
|
||||
"Indicates that the profile is currently inactive and that its user has moved to a new account.",
|
||||
example: null,
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#moved",
|
||||
},
|
||||
});
|
||||
},
|
||||
/* Versia Server API extension */
|
||||
roles: z.array(Role).openapi({
|
||||
roles: z.array(Role).meta({
|
||||
description: "Roles assigned to the account.",
|
||||
}),
|
||||
mute_expires_at: z.string().datetime().nullable().openapi({
|
||||
mute_expires_at: z.iso.datetime().nullable().meta({
|
||||
description: "When a timed mute will expire, if applicable.",
|
||||
example: "2025-03-01T14:00:00.000Z",
|
||||
}),
|
||||
})
|
||||
.openapi({ ref: "BaseAccount" });
|
||||
|
||||
export const Account = BaseAccount.extend({
|
||||
moved: BaseAccount.nullable()
|
||||
.optional()
|
||||
.openapi({
|
||||
description:
|
||||
"Indicates that the profile is currently inactive and that its user has moved to a new account.",
|
||||
example: null,
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Account/#moved",
|
||||
},
|
||||
}),
|
||||
}).openapi({ ref: "Account" });
|
||||
.meta({ id: "Account" });
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const Appeal = z
|
||||
.object({
|
||||
text: z.string().openapi({
|
||||
text: z.string().meta({
|
||||
description:
|
||||
"Text of the appeal from the moderated account to the moderators.",
|
||||
example: "I believe this action was taken in error.",
|
||||
}),
|
||||
state: z.enum(["approved", "rejected", "pending"]).openapi({
|
||||
state: z.enum(["approved", "rejected", "pending"]).meta({
|
||||
description:
|
||||
"State of the appeal. 'approved' = The appeal has been approved by a moderator, 'rejected' = The appeal has been rejected by a moderator, 'pending' = The appeal has been submitted, but neither approved nor rejected yet.",
|
||||
example: "pending",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Appeal against a moderation action.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Appeal",
|
||||
},
|
||||
ref: "Appeal",
|
||||
id: "Appeal",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const Application = z
|
||||
.object({
|
||||
|
|
@ -7,7 +7,7 @@ export const Application = z
|
|||
.trim()
|
||||
.min(1)
|
||||
.max(200)
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The name of your application.",
|
||||
example: "Test Application",
|
||||
externalDocs: {
|
||||
|
|
@ -17,7 +17,7 @@ export const Application = z
|
|||
website: z
|
||||
.string()
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The website associated with your application.",
|
||||
example: "https://app.example",
|
||||
externalDocs: {
|
||||
|
|
@ -27,7 +27,7 @@ export const Application = z
|
|||
scopes: z
|
||||
.array(z.string())
|
||||
.default(["read"])
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"The scopes for your application. This is the registered scopes string split on whitespace.",
|
||||
example: ["read", "write", "push"],
|
||||
|
|
@ -37,22 +37,18 @@ export const Application = z
|
|||
}),
|
||||
redirect_uris: z
|
||||
.array(
|
||||
z
|
||||
.string()
|
||||
.url()
|
||||
.or(z.literal("urn:ietf:wg:oauth:2.0:oob"))
|
||||
.openapi({
|
||||
description: "URL or 'urn:ietf:wg:oauth:2.0:oob'",
|
||||
}),
|
||||
z.url().or(z.literal("urn:ietf:wg:oauth:2.0:oob")).meta({
|
||||
description: "URL or 'urn:ietf:wg:oauth:2.0:oob'",
|
||||
}),
|
||||
)
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"The registered redirection URI(s) for your application.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Application/#redirect_uris",
|
||||
},
|
||||
}),
|
||||
redirect_uri: z.string().openapi({
|
||||
redirect_uri: z.string().meta({
|
||||
deprecated: true,
|
||||
description:
|
||||
"The registered redirection URI(s) for your application. May contain \\n characters when multiple redirect URIs are registered.",
|
||||
|
|
@ -61,30 +57,30 @@ export const Application = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
ref: "Application",
|
||||
.meta({
|
||||
id: "Application",
|
||||
});
|
||||
|
||||
export const CredentialApplication = Application.extend({
|
||||
client_id: z.string().openapi({
|
||||
client_id: z.string().meta({
|
||||
description: "Client ID key, to be used for obtaining OAuth tokens",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/CredentialApplication/#client_id",
|
||||
},
|
||||
}),
|
||||
client_secret: z.string().openapi({
|
||||
client_secret: z.string().meta({
|
||||
description: "Client secret key, to be used for obtaining OAuth tokens",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/CredentialApplication/#client_secret",
|
||||
},
|
||||
}),
|
||||
client_secret_expires_at: z.string().openapi({
|
||||
client_secret_expires_at: z.string().meta({
|
||||
description:
|
||||
"When the client secret key will expire at, presently this always returns 0 indicating that OAuth Clients do not expire",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/CredentialApplication/#client_secret_expires_at",
|
||||
},
|
||||
}),
|
||||
}).openapi({
|
||||
ref: "CredentialApplication",
|
||||
}).meta({
|
||||
id: "CredentialApplication",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Id } from "./common.ts";
|
||||
|
||||
export const Attachment = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "The ID of the attachment in the database.",
|
||||
example: "8c33d4c6-2292-4f4d-945d-261836e09647",
|
||||
}),
|
||||
type: z.enum(["unknown", "image", "gifv", "video", "audio"]).openapi({
|
||||
type: z.enum(["unknown", "image", "gifv", "video", "audio"]).meta({
|
||||
description:
|
||||
"The type of the attachment. 'unknown' = unsupported or unrecognized file type, 'image' = Static image, 'gifv' = Looping, soundless animation, 'video' = Video clip, 'audio' = Audio track.",
|
||||
example: "image",
|
||||
}),
|
||||
url: z.string().url().openapi({
|
||||
url: z.url().meta({
|
||||
description: "The location of the original full-size attachment.",
|
||||
example:
|
||||
"https://files.mastodon.social/media_attachments/files/022/345/792/original/57859aede991da25.jpeg",
|
||||
}),
|
||||
preview_url: z.string().url().nullable().openapi({
|
||||
preview_url: z.url().nullable().meta({
|
||||
description:
|
||||
"The location of a scaled-down preview of the attachment.",
|
||||
example:
|
||||
"https://files.mastodon.social/media_attachments/files/022/345/792/small/57859aede991da25.jpeg",
|
||||
}),
|
||||
remote_url: z.string().url().nullable().openapi({
|
||||
remote_url: z.url().nullable().meta({
|
||||
description:
|
||||
"The location of the full-size original attachment on the remote website, or null if the attachment is local.",
|
||||
example: null,
|
||||
}),
|
||||
meta: z.record(z.any()).openapi({
|
||||
meta: z.any().meta({
|
||||
description:
|
||||
"Metadata. May contain subtrees like 'small' and 'original', and possibly a 'focus' object for smart thumbnail cropping.",
|
||||
example: {
|
||||
|
|
@ -50,22 +50,22 @@ export const Attachment = z
|
|||
},
|
||||
},
|
||||
}),
|
||||
description: z.string().trim().nullable().openapi({
|
||||
description: z.string().trim().nullable().meta({
|
||||
description:
|
||||
"Alternate text that describes what is in the media attachment, to be used for the visually impaired or when media attachments do not load.",
|
||||
example: "test media description",
|
||||
}),
|
||||
blurhash: z.string().nullable().openapi({
|
||||
blurhash: z.string().nullable().meta({
|
||||
description:
|
||||
"A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.",
|
||||
example: "UFBWY:8_0Jxv4mx]t8t64.%M-:IUWGWAt6M}",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents a file or media attachment that can be added to a status.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Attachment",
|
||||
},
|
||||
ref: "Attachment",
|
||||
id: "Attachment",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,97 +1,88 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Account } from "./account.ts";
|
||||
|
||||
export const PreviewCardAuthor = z
|
||||
.object({
|
||||
name: z.string().openapi({
|
||||
name: z.string().meta({
|
||||
description: "The original resource author’s name.",
|
||||
example: "The Doubleclicks",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCardAuthor/#name",
|
||||
},
|
||||
}),
|
||||
url: z
|
||||
.string()
|
||||
.url()
|
||||
.openapi({
|
||||
description: "A link to the author of the original resource.",
|
||||
example: "https://www.youtube.com/user/thedoubleclicks",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCardAuthor/#url",
|
||||
},
|
||||
}),
|
||||
account: Account.nullable().openapi({
|
||||
url: z.url().meta({
|
||||
description: "A link to the author of the original resource.",
|
||||
example: "https://www.youtube.com/user/thedoubleclicks",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCardAuthor/#url",
|
||||
},
|
||||
}),
|
||||
account: Account.nullable().meta({
|
||||
description: "The fediverse account of the author.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCardAuthor/#account",
|
||||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
ref: "PreviewCardAuthor",
|
||||
.meta({
|
||||
id: "PreviewCardAuthor",
|
||||
});
|
||||
|
||||
export const PreviewCard = z
|
||||
.object({
|
||||
url: z
|
||||
.string()
|
||||
.url()
|
||||
.openapi({
|
||||
description: "Location of linked resource.",
|
||||
example: "https://www.youtube.com/watch?v=OMv_EPMED8Y",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCard/#url",
|
||||
},
|
||||
}),
|
||||
url: z.url().meta({
|
||||
description: "Location of linked resource.",
|
||||
example: "https://www.youtube.com/watch?v=OMv_EPMED8Y",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCard/#url",
|
||||
},
|
||||
}),
|
||||
title: z
|
||||
.string()
|
||||
.min(1)
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Title of linked resource.",
|
||||
example: "♪ Brand New Friend (Christmas Song!)",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCard/#title",
|
||||
},
|
||||
}),
|
||||
description: z.string().openapi({
|
||||
description: z.string().meta({
|
||||
description: "Description of preview.",
|
||||
example: "",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCard/#description",
|
||||
},
|
||||
}),
|
||||
type: z.enum(["link", "photo", "video"]).openapi({
|
||||
type: z.enum(["link", "photo", "video"]).meta({
|
||||
description: "The type of the preview card.",
|
||||
example: "video",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCard/#type",
|
||||
},
|
||||
}),
|
||||
authors: z.array(PreviewCardAuthor).openapi({
|
||||
authors: z.array(PreviewCardAuthor).meta({
|
||||
description:
|
||||
"Fediverse account of the authors of the original resource.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCard/#authors",
|
||||
},
|
||||
}),
|
||||
provider_name: z.string().openapi({
|
||||
provider_name: z.string().meta({
|
||||
description: "The provider of the original resource.",
|
||||
example: "YouTube",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCard/#provider_name",
|
||||
},
|
||||
}),
|
||||
provider_url: z
|
||||
.string()
|
||||
.url()
|
||||
.openapi({
|
||||
description: "A link to the provider of the original resource.",
|
||||
example: "https://www.youtube.com/",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCard/#provider_url",
|
||||
},
|
||||
}),
|
||||
html: z.string().openapi({
|
||||
provider_url: z.url().meta({
|
||||
description: "A link to the provider of the original resource.",
|
||||
example: "https://www.youtube.com/",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCard/#provider_url",
|
||||
},
|
||||
}),
|
||||
html: z.string().meta({
|
||||
description: "HTML to be used for generating the preview card.",
|
||||
example:
|
||||
'<iframe width="480" height="270" src="https://www.youtube.com/embed/OMv_EPMED8Y?feature=oembed" frameborder="0" allowfullscreen=""></iframe>',
|
||||
|
|
@ -102,7 +93,7 @@ export const PreviewCard = z
|
|||
width: z
|
||||
.number()
|
||||
.int()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Width of preview, in pixels.",
|
||||
example: 480,
|
||||
externalDocs: {
|
||||
|
|
@ -112,7 +103,7 @@ export const PreviewCard = z
|
|||
height: z
|
||||
.number()
|
||||
.int()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Height of preview, in pixels.",
|
||||
example: 270,
|
||||
externalDocs: {
|
||||
|
|
@ -120,10 +111,9 @@ export const PreviewCard = z
|
|||
},
|
||||
}),
|
||||
image: z
|
||||
.string()
|
||||
.url()
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Preview thumbnail.",
|
||||
example:
|
||||
"https://cdn.versia.social/preview_cards/images/014/179/145/original/9cf4b7cf5567b569.jpeg",
|
||||
|
|
@ -131,21 +121,18 @@ export const PreviewCard = z
|
|||
url: "https://docs.joinmastodon.org/entities/PreviewCard/#image",
|
||||
},
|
||||
}),
|
||||
embed_url: z
|
||||
.string()
|
||||
.url()
|
||||
.openapi({
|
||||
description: "Used for photo embeds, instead of custom html.",
|
||||
example:
|
||||
"https://live.staticflickr.com/65535/49088768431_6a4322b3bb_b.jpg",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCard/#embed_url",
|
||||
},
|
||||
}),
|
||||
embed_url: z.url().meta({
|
||||
description: "Used for photo embeds, instead of custom html.",
|
||||
example:
|
||||
"https://live.staticflickr.com/65535/49088768431_6a4322b3bb_b.jpg",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCard/#embed_url",
|
||||
},
|
||||
}),
|
||||
blurhash: z
|
||||
.string()
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.",
|
||||
example: "UvK0HNkV,:s9xBR%njog0fo2W=WBS5ozofV@",
|
||||
|
|
@ -154,11 +141,11 @@ export const PreviewCard = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents a rich preview card that is generated using OpenGraph tags from a URL.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PreviewCard",
|
||||
},
|
||||
ref: "PreviewCard",
|
||||
id: "PreviewCard",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,22 +1,17 @@
|
|||
import ISO6391 from "iso-639-1";
|
||||
import { z } from "zod";
|
||||
import "zod-openapi/extend";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const Id = z.string().uuid();
|
||||
export const Id = z.uuid();
|
||||
|
||||
export const iso631 = z
|
||||
.enum(ISO6391.getAllCodes() as [string, ...string[]])
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "ISO 639-1 language code",
|
||||
example: "en",
|
||||
externalDocs: {
|
||||
url: "https://en.wikipedia.org/wiki/List_of_ISO_639-1_language_codes",
|
||||
},
|
||||
ref: "ISO631",
|
||||
id: "ISO631",
|
||||
});
|
||||
|
||||
export const zBoolean = z
|
||||
.string()
|
||||
.transform((v) => ["true", "1", "on"].includes(v.toLowerCase()))
|
||||
.openapi({ type: "boolean" })
|
||||
.or(z.boolean());
|
||||
export const zBoolean = z.stringbool().or(z.boolean());
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Status } from "./status.ts";
|
||||
|
||||
export const Context = z
|
||||
.object({
|
||||
ancestors: z.array(Status).openapi({
|
||||
ancestors: z.array(Status).meta({
|
||||
description: "Parents in the thread.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Context/#ancestors",
|
||||
},
|
||||
}),
|
||||
descendants: z.array(Status).openapi({
|
||||
descendants: z.array(Status).meta({
|
||||
description: "Children in the thread.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Context/#descendants",
|
||||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents the tree around a given status. Used for reconstructing threads of statuses.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Context/#context",
|
||||
},
|
||||
ref: "Context",
|
||||
id: "Context",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { emojiRegex } from "../regex.ts";
|
||||
import { Id, zBoolean } from "./common.ts";
|
||||
|
||||
export const CustomEmoji = z
|
||||
.object({
|
||||
/* Versia Server API extension */
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "ID of the custom emoji in the database.",
|
||||
example: "af9ccd29-c689-477f-aa27-d7d95fd8fb05",
|
||||
}),
|
||||
|
|
@ -17,36 +17,30 @@ export const CustomEmoji = z
|
|||
emojiRegex,
|
||||
"Shortcode must only contain letters (any case), numbers, dashes or underscores.",
|
||||
)
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The name of the custom emoji.",
|
||||
example: "blobaww",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/CustomEmoji/#shortcode",
|
||||
},
|
||||
}),
|
||||
url: z
|
||||
.string()
|
||||
.url()
|
||||
.openapi({
|
||||
description: "A link to the custom emoji.",
|
||||
example:
|
||||
"https://cdn.versia.social/emojis/images/000/011/739/original/blobaww.png",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/CustomEmoji/#url",
|
||||
},
|
||||
}),
|
||||
static_url: z
|
||||
.string()
|
||||
.url()
|
||||
.openapi({
|
||||
description: "A link to a static copy of the custom emoji.",
|
||||
example:
|
||||
"https://cdn.versia.social/emojis/images/000/011/739/static/blobaww.png",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/CustomEmoji/#static_url",
|
||||
},
|
||||
}),
|
||||
visible_in_picker: z.boolean().openapi({
|
||||
url: z.url().meta({
|
||||
description: "A link to the custom emoji.",
|
||||
example:
|
||||
"https://cdn.versia.social/emojis/images/000/011/739/original/blobaww.png",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/CustomEmoji/#url",
|
||||
},
|
||||
}),
|
||||
static_url: z.url().meta({
|
||||
description: "A link to a static copy of the custom emoji.",
|
||||
example:
|
||||
"https://cdn.versia.social/emojis/images/000/011/739/static/blobaww.png",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/CustomEmoji/#static_url",
|
||||
},
|
||||
}),
|
||||
visible_in_picker: z.boolean().meta({
|
||||
description:
|
||||
"Whether this Emoji should be visible in the picker or unlisted.",
|
||||
example: true,
|
||||
|
|
@ -59,7 +53,7 @@ export const CustomEmoji = z
|
|||
.trim()
|
||||
.max(64)
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Used for sorting custom emoji in the picker.",
|
||||
example: "Blobs",
|
||||
externalDocs: {
|
||||
|
|
@ -67,7 +61,7 @@ export const CustomEmoji = z
|
|||
},
|
||||
}),
|
||||
/* Versia Server API extension */
|
||||
global: zBoolean.openapi({
|
||||
global: zBoolean.meta({
|
||||
description: "Whether this emoji is visible to all users.",
|
||||
example: false,
|
||||
}),
|
||||
|
|
@ -75,7 +69,7 @@ export const CustomEmoji = z
|
|||
description: z
|
||||
.string()
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Emoji description for users using screen readers.",
|
||||
example: "A cute blob.",
|
||||
|
|
@ -84,10 +78,10 @@ export const CustomEmoji = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Represents a custom emoji.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/CustomEmoji",
|
||||
},
|
||||
ref: "CustomEmoji",
|
||||
id: "CustomEmoji",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,19 +1,16 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const ExtendedDescription = z
|
||||
.object({
|
||||
updated_at: z
|
||||
.string()
|
||||
.datetime()
|
||||
.openapi({
|
||||
description:
|
||||
"A timestamp of when the extended description was last updated.",
|
||||
example: "2025-01-12T13:11:00Z",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/ExtendedDescription/#updated_at",
|
||||
},
|
||||
}),
|
||||
content: z.string().openapi({
|
||||
updated_at: z.iso.datetime().meta({
|
||||
description:
|
||||
"A timestamp of when the extended description was last updated.",
|
||||
example: "2025-01-12T13:11:00Z",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/ExtendedDescription/#updated_at",
|
||||
},
|
||||
}),
|
||||
content: z.string().meta({
|
||||
description:
|
||||
"The rendered HTML content of the extended description.",
|
||||
example: "<p>We love casting spells.</p>",
|
||||
|
|
@ -22,11 +19,11 @@ export const ExtendedDescription = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents an extended description for the instance, to be shown on its about page.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/ExtendedDescription",
|
||||
},
|
||||
ref: "ExtendedDescription",
|
||||
id: "ExtendedDescription",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Account } from "./account.ts";
|
||||
|
||||
export const FamiliarFollowers = z
|
||||
.object({
|
||||
id: Account.shape.id.openapi({
|
||||
id: Account.shape.id.meta({
|
||||
description: "The ID of the Account in the database.",
|
||||
example: "48214efb-1f3c-459a-abfa-618a5aeb2f7a",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/FamiliarFollowers/#id",
|
||||
},
|
||||
}),
|
||||
accounts: z.array(Account).openapi({
|
||||
accounts: z.array(Account).meta({
|
||||
description: "Accounts you follow that also follow this account.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/FamiliarFollowers/#accounts",
|
||||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents a subset of your follows who also follow some other user.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/FamiliarFollowers",
|
||||
},
|
||||
ref: "FamiliarFollowers",
|
||||
id: "FamiliarFollowers",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Id, zBoolean } from "./common.ts";
|
||||
|
||||
export const FilterStatus = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "The ID of the FilterStatus in the database.",
|
||||
example: "3b19ed7c-0c4b-45e1-8c75-e21dfc8e86c3",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/FilterStatus/#id",
|
||||
},
|
||||
}),
|
||||
status_id: Id.openapi({
|
||||
status_id: Id.meta({
|
||||
description: "The ID of the Status that will be filtered.",
|
||||
example: "4f941ac8-295c-4c2d-9300-82c162ac8028",
|
||||
externalDocs: {
|
||||
|
|
@ -18,32 +18,32 @@ export const FilterStatus = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents a status ID that, if matched, should cause the filter action to be taken.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/FilterStatus",
|
||||
},
|
||||
ref: "FilterStatus",
|
||||
id: "FilterStatus",
|
||||
});
|
||||
|
||||
export const FilterKeyword = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "The ID of the FilterKeyword in the database.",
|
||||
example: "ca921e60-5b96-4686-90f3-d7cc420d7391",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/FilterKeyword/#id",
|
||||
},
|
||||
}),
|
||||
keyword: z.string().openapi({
|
||||
keyword: z.string().meta({
|
||||
description: "The phrase to be matched against.",
|
||||
example: "badword",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/FilterKeyword/#keyword",
|
||||
},
|
||||
}),
|
||||
whole_word: zBoolean.openapi({
|
||||
whole_word: zBoolean.meta({
|
||||
description:
|
||||
"Should the filter consider word boundaries? See implementation guidelines for filters.",
|
||||
example: false,
|
||||
|
|
@ -52,18 +52,18 @@ export const FilterKeyword = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents a keyword that, if matched, should cause the filter action to be taken.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/FilterKeyword",
|
||||
},
|
||||
ref: "FilterKeyword",
|
||||
id: "FilterKeyword",
|
||||
});
|
||||
|
||||
export const Filter = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "The ID of the Filter in the database.",
|
||||
example: "6b8fa22f-b128-43c2-9a1f-3c0499ef3a51",
|
||||
externalDocs: {
|
||||
|
|
@ -75,7 +75,7 @@ export const Filter = z
|
|||
.trim()
|
||||
.min(1)
|
||||
.max(255)
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "A title given by the user to name the filter.",
|
||||
example: "Test filter",
|
||||
externalDocs: {
|
||||
|
|
@ -93,7 +93,7 @@ export const Filter = z
|
|||
]),
|
||||
)
|
||||
.default([])
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"The contexts in which the filter should be applied.",
|
||||
example: ["home"],
|
||||
|
|
@ -104,7 +104,7 @@ export const Filter = z
|
|||
expires_at: z
|
||||
.string()
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "When the filter should no longer be applied.",
|
||||
example: "2026-09-20T17:27:39.296Z",
|
||||
externalDocs: {
|
||||
|
|
@ -114,7 +114,7 @@ export const Filter = z
|
|||
filter_action: z
|
||||
.enum(["warn", "hide"])
|
||||
.default("warn")
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"The action to be taken when a status matches this filter.",
|
||||
example: "warn",
|
||||
|
|
@ -122,31 +122,31 @@ export const Filter = z
|
|||
url: "https://docs.joinmastodon.org/entities/Filter/#filter_action",
|
||||
},
|
||||
}),
|
||||
keywords: z.array(FilterKeyword).openapi({
|
||||
keywords: z.array(FilterKeyword).meta({
|
||||
description: "The keywords grouped under this filter.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Filter/#keywords",
|
||||
},
|
||||
}),
|
||||
statuses: z.array(FilterStatus).openapi({
|
||||
statuses: z.array(FilterStatus).meta({
|
||||
description: "The statuses grouped under this filter.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Filter/#statuses",
|
||||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents a user-defined filter for determining which statuses should not be shown to the user.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Filter",
|
||||
},
|
||||
ref: "Filter",
|
||||
id: "Filter",
|
||||
});
|
||||
|
||||
export const FilterResult = z
|
||||
.object({
|
||||
filter: Filter.openapi({
|
||||
filter: Filter.meta({
|
||||
description: "The filter that was matched.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/FilterResult/#filter",
|
||||
|
|
@ -155,7 +155,7 @@ export const FilterResult = z
|
|||
keyword_matches: z
|
||||
.array(z.string())
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The keyword within the filter that was matched.",
|
||||
example: ["badword"],
|
||||
externalDocs: {
|
||||
|
|
@ -165,7 +165,7 @@ export const FilterResult = z
|
|||
status_matches: z
|
||||
.array(Id)
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"The status ID within the filter that was matched.",
|
||||
example: ["3819515a-5ceb-4078-8524-c939e38dcf8f"],
|
||||
|
|
@ -174,11 +174,11 @@ export const FilterResult = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents a filter whose keywords matched a given status.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/FilterResult",
|
||||
},
|
||||
ref: "FilterResult",
|
||||
id: "FilterResult",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Instance } from "./instance.ts";
|
||||
import { SSOConfig } from "./versia.ts";
|
||||
|
||||
|
|
@ -7,7 +7,7 @@ export const InstanceV1 = z
|
|||
uri: Instance.shape.domain,
|
||||
title: Instance.shape.title,
|
||||
short_description: Instance.shape.description,
|
||||
description: z.string().openapi({
|
||||
description: z.string().meta({
|
||||
description: "An HTML-permitted description of the site.",
|
||||
example: "<p>Join the world's smallest social network.</p>",
|
||||
}),
|
||||
|
|
@ -20,29 +20,29 @@ export const InstanceV1 = z
|
|||
streaming_api:
|
||||
Instance.shape.configuration.shape.urls.shape.streaming,
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "URLs of interest for clients apps.",
|
||||
}),
|
||||
stats: z
|
||||
.object({
|
||||
user_count: z.number().openapi({
|
||||
user_count: z.number().meta({
|
||||
description: "Total users on this instance.",
|
||||
example: 812303,
|
||||
}),
|
||||
status_count: z.number().openapi({
|
||||
status_count: z.number().meta({
|
||||
description: "Total statuses on this instance.",
|
||||
example: 38151616,
|
||||
}),
|
||||
domain_count: z.number().openapi({
|
||||
domain_count: z.number().meta({
|
||||
description: "Total domains discovered by this instance.",
|
||||
example: 25255,
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Statistics about how much information the instance contains.",
|
||||
}),
|
||||
thumbnail: z.string().url().nullable().openapi({
|
||||
thumbnail: z.url().nullable().meta({
|
||||
description: "Banner image for the website.",
|
||||
example:
|
||||
"https://files.mastodon.social/site_uploads/files/000/000/001/original/vlcsnap-2018-08-27-16h43m11s127.png",
|
||||
|
|
@ -50,7 +50,7 @@ export const InstanceV1 = z
|
|||
languages: Instance.shape.languages,
|
||||
registrations: Instance.shape.registrations.shape.enabled,
|
||||
approval_required: Instance.shape.registrations.shape.approval_required,
|
||||
invites_enabled: z.boolean().openapi({
|
||||
invites_enabled: z.boolean().meta({
|
||||
description: "Whether invites are enabled.",
|
||||
example: true,
|
||||
}),
|
||||
|
|
@ -62,7 +62,7 @@ export const InstanceV1 = z
|
|||
Instance.shape.configuration.shape.accounts.shape
|
||||
.max_featured_tags,
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Limits related to accounts.",
|
||||
}),
|
||||
statuses: z
|
||||
|
|
@ -77,7 +77,7 @@ export const InstanceV1 = z
|
|||
Instance.shape.configuration.shape.statuses.shape
|
||||
.characters_reserved_per_url,
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Limits related to authoring statuses.",
|
||||
}),
|
||||
media_attachments: z
|
||||
|
|
@ -101,7 +101,7 @@ export const InstanceV1 = z
|
|||
Instance.shape.configuration.shape.media_attachments
|
||||
.shape.video_matrix_limit,
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Hints for which attachments will be accepted.",
|
||||
}),
|
||||
|
|
@ -120,11 +120,11 @@ export const InstanceV1 = z
|
|||
Instance.shape.configuration.shape.polls.shape
|
||||
.max_expiration,
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Limits related to polls.",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Configured values and limits for this website.",
|
||||
}),
|
||||
contact_account: Instance.shape.contact.shape.account,
|
||||
|
|
@ -132,11 +132,11 @@ export const InstanceV1 = z
|
|||
/* Versia Server API extension */
|
||||
sso: SSOConfig,
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents the software instance of Versia Server running on this domain.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/V1_Instance",
|
||||
},
|
||||
ref: "InstanceV1",
|
||||
id: "InstanceV1",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Account } from "./account.ts";
|
||||
import { iso631 } from "./common.ts";
|
||||
import { Rule } from "./rule.ts";
|
||||
|
|
@ -6,50 +6,50 @@ import { SSOConfig } from "./versia.ts";
|
|||
|
||||
const InstanceIcon = z
|
||||
.object({
|
||||
src: z.string().url().openapi({
|
||||
src: z.url().meta({
|
||||
description: "The URL of this icon.",
|
||||
example:
|
||||
"https://files.mastodon.social/site_uploads/files/000/000/003/36/accf17b0104f18e5.png",
|
||||
}),
|
||||
size: z.string().openapi({
|
||||
size: z.string().meta({
|
||||
description:
|
||||
"The size of this icon (in the form of 12x34, where 12 is the width and 34 is the height of the icon).",
|
||||
example: "36x36",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/InstanceIcon",
|
||||
},
|
||||
ref: "InstanceIcon",
|
||||
id: "InstanceIcon",
|
||||
});
|
||||
|
||||
export const Instance = z
|
||||
.object({
|
||||
domain: z.string().openapi({
|
||||
domain: z.string().meta({
|
||||
description: "The domain name of the instance.",
|
||||
example: "versia.social",
|
||||
}),
|
||||
title: z.string().openapi({
|
||||
title: z.string().meta({
|
||||
description: "The title of the website.",
|
||||
example: "Versia Social • Now with 100% more blobs!",
|
||||
}),
|
||||
version: z.string().openapi({
|
||||
version: z.string().meta({
|
||||
description:
|
||||
"Mastodon version that the API is compatible with. Used for compatibility with Mastodon clients.",
|
||||
example: "4.3.0+glitch",
|
||||
}),
|
||||
/* Versia Server API extension */
|
||||
versia_version: z.string().openapi({
|
||||
versia_version: z.string().meta({
|
||||
description: "Versia Server version.",
|
||||
example: "0.8.0",
|
||||
}),
|
||||
source_url: z.string().url().openapi({
|
||||
source_url: z.url().meta({
|
||||
description:
|
||||
"The URL for the source code of the software running on this instance, in keeping with AGPL license requirements.",
|
||||
example: "https://github.com/versia-pub/server",
|
||||
}),
|
||||
description: z.string().openapi({
|
||||
description: z.string().meta({
|
||||
description:
|
||||
"A short, plain-text description defined by the admin.",
|
||||
example: "The flagship Versia Server instance. Join for free hugs!",
|
||||
|
|
@ -58,74 +58,74 @@ export const Instance = z
|
|||
.object({
|
||||
users: z
|
||||
.object({
|
||||
active_month: z.number().openapi({
|
||||
active_month: z.number().meta({
|
||||
description:
|
||||
"The number of active users in the past 4 weeks.",
|
||||
example: 1_261,
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Usage data related to users on this instance.",
|
||||
}),
|
||||
})
|
||||
.openapi({ description: "Usage data for this instance." }),
|
||||
.meta({ description: "Usage data for this instance." }),
|
||||
thumbnail: z
|
||||
.object({
|
||||
url: z.string().url().openapi({
|
||||
url: z.url().meta({
|
||||
description: "The URL for the thumbnail image.",
|
||||
example:
|
||||
"https://files.mastodon.social/site_uploads/files/000/000/001/@1x/57c12f441d083cde.png",
|
||||
}),
|
||||
blurhash: z.string().optional().openapi({
|
||||
blurhash: z.string().optional().meta({
|
||||
description:
|
||||
"A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.",
|
||||
example: "UUKJMXv|x]t7^*t7Rjaz^jazRjaz",
|
||||
}),
|
||||
versions: z
|
||||
.object({
|
||||
"@1x": z.string().url().optional().openapi({
|
||||
"@1x": z.url().optional().meta({
|
||||
description:
|
||||
"The URL for the thumbnail image at 1x resolution.",
|
||||
}),
|
||||
"@2x": z.string().url().optional().openapi({
|
||||
"@2x": z.url().optional().meta({
|
||||
description:
|
||||
"The URL for the thumbnail image at 2x resolution.",
|
||||
}),
|
||||
})
|
||||
.optional()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Links to scaled resolution images, for high DPI screens.",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "An image used to represent this instance.",
|
||||
}),
|
||||
/* Versia Server API extension */
|
||||
banner: z
|
||||
.object({
|
||||
url: z.string().url().openapi({
|
||||
url: z.url().meta({
|
||||
description: "The URL for the banner image.",
|
||||
example:
|
||||
"https://files.mastodon.social/site_uploads/files/000/000/001/@1x/57c12f441d083cde.png",
|
||||
}),
|
||||
blurhash: z.string().optional().openapi({
|
||||
blurhash: z.string().optional().meta({
|
||||
description:
|
||||
"A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.",
|
||||
example: "UUKJMXv|x]t7^*t7Rjaz^jazRjaz",
|
||||
}),
|
||||
})
|
||||
.optional()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"A wide banner image used to represent this instance.",
|
||||
}),
|
||||
icon: z.array(InstanceIcon).openapi({
|
||||
icon: z.array(InstanceIcon).meta({
|
||||
description:
|
||||
"The list of available size variants for this instance configured icon.",
|
||||
}),
|
||||
languages: z.array(iso631).openapi({
|
||||
languages: z.array(iso631).meta({
|
||||
description: "Primary languages of the website and its staff.",
|
||||
example: ["en"],
|
||||
}),
|
||||
|
|
@ -133,63 +133,63 @@ export const Instance = z
|
|||
.object({
|
||||
urls: z
|
||||
.object({
|
||||
streaming: z.string().url().openapi({
|
||||
streaming: z.url().meta({
|
||||
description:
|
||||
"The Websockets URL for connecting to the streaming API.",
|
||||
example: "wss://versia.social",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "URLs of interest for clients apps.",
|
||||
}),
|
||||
vapid: z
|
||||
.object({
|
||||
public_key: z.string().openapi({
|
||||
public_key: z.string().meta({
|
||||
description:
|
||||
"The instance's VAPID public key, used for push notifications, the same as WebPushSubscription#server_key.",
|
||||
example:
|
||||
"BCkMmVdKDnKYwzVCDC99Iuc9GvId-x7-kKtuHnLgfF98ENiZp_aj-UNthbCdI70DqN1zUVis-x0Wrot2sBagkMc=",
|
||||
}),
|
||||
})
|
||||
.openapi({ description: "VAPID configuration." }),
|
||||
.meta({ description: "VAPID configuration." }),
|
||||
accounts: z
|
||||
.object({
|
||||
max_featured_tags: z.number().openapi({
|
||||
max_featured_tags: z.number().meta({
|
||||
description:
|
||||
"The maximum number of featured tags allowed for each account.",
|
||||
example: 10,
|
||||
}),
|
||||
max_pinned_statuses: z.number().openapi({
|
||||
max_pinned_statuses: z.number().meta({
|
||||
description:
|
||||
"The maximum number of pinned statuses for each account.",
|
||||
example: 4,
|
||||
}),
|
||||
/* Versia Server API extension */
|
||||
max_displayname_characters: z.number().openapi({
|
||||
max_displayname_characters: z.number().meta({
|
||||
description:
|
||||
"The maximum number of characters allowed in a display name.",
|
||||
example: 30,
|
||||
}),
|
||||
/* Versia Server API extension */
|
||||
max_username_characters: z.number().openapi({
|
||||
max_username_characters: z.number().meta({
|
||||
description:
|
||||
"The maximum number of characters allowed in a username.",
|
||||
example: 30,
|
||||
}),
|
||||
/* Versia Server API extension */
|
||||
max_note_characters: z.number().openapi({
|
||||
max_note_characters: z.number().meta({
|
||||
description:
|
||||
"The maximum number of characters allowed in an account's bio/note.",
|
||||
example: 500,
|
||||
}),
|
||||
/* Versia Server API extension */
|
||||
avatar_limit: z.number().openapi({
|
||||
avatar_limit: z.number().meta({
|
||||
description:
|
||||
"The maximum size of an avatar image, in bytes.",
|
||||
example: 1048576,
|
||||
}),
|
||||
/* Versia Server API extension */
|
||||
header_limit: z.number().openapi({
|
||||
header_limit: z.number().meta({
|
||||
description:
|
||||
"The maximum size of a header image, in bytes.",
|
||||
example: 2097152,
|
||||
|
|
@ -197,206 +197,206 @@ export const Instance = z
|
|||
/* Versia Server API extension */
|
||||
fields: z
|
||||
.object({
|
||||
max_fields: z.number().openapi({
|
||||
max_fields: z.number().meta({
|
||||
description:
|
||||
"The maximum number of fields allowed per account.",
|
||||
example: 4,
|
||||
}),
|
||||
max_name_characters: z.number().openapi({
|
||||
max_name_characters: z.number().meta({
|
||||
description:
|
||||
"The maximum number of characters allowed in a field name.",
|
||||
example: 30,
|
||||
}),
|
||||
max_value_characters: z.number().openapi({
|
||||
max_value_characters: z.number().meta({
|
||||
description:
|
||||
"The maximum number of characters allowed in a field value.",
|
||||
example: 100,
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Limits related to account fields.",
|
||||
}),
|
||||
})
|
||||
.openapi({ description: "Limits related to accounts." }),
|
||||
.meta({ description: "Limits related to accounts." }),
|
||||
statuses: z
|
||||
.object({
|
||||
max_characters: z.number().openapi({
|
||||
max_characters: z.number().meta({
|
||||
description:
|
||||
"The maximum number of allowed characters per status.",
|
||||
example: 500,
|
||||
}),
|
||||
max_media_attachments: z.number().openapi({
|
||||
max_media_attachments: z.number().meta({
|
||||
description:
|
||||
"The maximum number of media attachments that can be added to a status.",
|
||||
example: 4,
|
||||
}),
|
||||
characters_reserved_per_url: z.number().openapi({
|
||||
characters_reserved_per_url: z.number().meta({
|
||||
description:
|
||||
"Each URL in a status will be assumed to be exactly this many characters.",
|
||||
example: 23,
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Limits related to authoring statuses.",
|
||||
}),
|
||||
media_attachments: z
|
||||
.object({
|
||||
supported_mime_types: z.array(z.string()).openapi({
|
||||
supported_mime_types: z.array(z.string()).meta({
|
||||
description:
|
||||
"Contains MIME types that can be uploaded.",
|
||||
example: ["image/jpeg", "image/png", "image/gif"],
|
||||
}),
|
||||
description_limit: z.number().openapi({
|
||||
description_limit: z.number().meta({
|
||||
description:
|
||||
"The maximum size of a description, in characters.",
|
||||
example: 1500,
|
||||
}),
|
||||
image_size_limit: z.number().openapi({
|
||||
image_size_limit: z.number().meta({
|
||||
description:
|
||||
"The maximum size of any uploaded image, in bytes.",
|
||||
example: 10485760,
|
||||
}),
|
||||
image_matrix_limit: z.number().openapi({
|
||||
image_matrix_limit: z.number().meta({
|
||||
description:
|
||||
"The maximum number of pixels (width times height) for image uploads.",
|
||||
example: 16777216,
|
||||
}),
|
||||
video_size_limit: z.number().openapi({
|
||||
video_size_limit: z.number().meta({
|
||||
description:
|
||||
"The maximum size of any uploaded video, in bytes.",
|
||||
example: 41943040,
|
||||
}),
|
||||
video_frame_rate_limit: z.number().openapi({
|
||||
video_frame_rate_limit: z.number().meta({
|
||||
description:
|
||||
"The maximum frame rate for any uploaded video.",
|
||||
example: 60,
|
||||
}),
|
||||
video_matrix_limit: z.number().openapi({
|
||||
video_matrix_limit: z.number().meta({
|
||||
description:
|
||||
"The maximum number of pixels (width times height) for video uploads.",
|
||||
example: 2304000,
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Hints for which attachments will be accepted.",
|
||||
}),
|
||||
/* Versia Server API extension */
|
||||
emojis: z
|
||||
.object({
|
||||
emoji_size_limit: z.number().openapi({
|
||||
emoji_size_limit: z.number().meta({
|
||||
description:
|
||||
"The maximum size of an emoji image, in bytes.",
|
||||
example: 1048576,
|
||||
}),
|
||||
max_shortcode_characters: z.number().openapi({
|
||||
max_shortcode_characters: z.number().meta({
|
||||
description:
|
||||
"The maximum number of characters allowed in an emoji shortcode.",
|
||||
example: 30,
|
||||
}),
|
||||
max_description_characters: z.number().openapi({
|
||||
max_description_characters: z.number().meta({
|
||||
description:
|
||||
"The maximum number of characters allowed in an emoji description.",
|
||||
example: 100,
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Limits related to custom emojis.",
|
||||
}),
|
||||
polls: z
|
||||
.object({
|
||||
max_options: z.number().openapi({
|
||||
max_options: z.number().meta({
|
||||
description:
|
||||
"Each poll is allowed to have up to this many options.",
|
||||
example: 4,
|
||||
}),
|
||||
max_characters_per_option: z.number().openapi({
|
||||
max_characters_per_option: z.number().meta({
|
||||
description:
|
||||
"Each poll option is allowed to have this many characters.",
|
||||
example: 50,
|
||||
}),
|
||||
min_expiration: z.number().openapi({
|
||||
min_expiration: z.number().meta({
|
||||
description:
|
||||
"The shortest allowed poll duration, in seconds.",
|
||||
example: 300,
|
||||
}),
|
||||
max_expiration: z.number().openapi({
|
||||
max_expiration: z.number().meta({
|
||||
description:
|
||||
"The longest allowed poll duration, in seconds.",
|
||||
example: 2629746,
|
||||
}),
|
||||
})
|
||||
.openapi({ description: "Limits related to polls." }),
|
||||
.meta({ description: "Limits related to polls." }),
|
||||
translation: z
|
||||
.object({
|
||||
enabled: z.boolean().openapi({
|
||||
enabled: z.boolean().meta({
|
||||
description:
|
||||
"Whether the Translations API is available on this instance.",
|
||||
example: true,
|
||||
}),
|
||||
})
|
||||
.openapi({ description: "Hints related to translation." }),
|
||||
.meta({ description: "Hints related to translation." }),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Configured values and limits for this website.",
|
||||
}),
|
||||
registrations: z
|
||||
.object({
|
||||
enabled: z.boolean().openapi({
|
||||
enabled: z.boolean().meta({
|
||||
description: "Whether registrations are enabled.",
|
||||
example: false,
|
||||
}),
|
||||
approval_required: z.boolean().openapi({
|
||||
approval_required: z.boolean().meta({
|
||||
description:
|
||||
"Whether registrations require moderator approval.",
|
||||
example: false,
|
||||
}),
|
||||
message: z.string().nullable().openapi({
|
||||
message: z.string().nullable().meta({
|
||||
description:
|
||||
"A custom message to be shown when registrations are closed.",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Information about registering for this website.",
|
||||
}),
|
||||
api_versions: z
|
||||
.object({
|
||||
mastodon: z.number().openapi({
|
||||
mastodon: z.number().meta({
|
||||
description:
|
||||
"API version number that this server implements.",
|
||||
example: 1,
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Information about which version of the API is implemented by this server.",
|
||||
}),
|
||||
contact: z
|
||||
.object({
|
||||
email: z.string().email().openapi({
|
||||
email: z.email().meta({
|
||||
description:
|
||||
"An email address that can be messaged regarding inquiries or issues.",
|
||||
example: "contact@versia.social",
|
||||
}),
|
||||
account: Account.nullable().openapi({
|
||||
account: Account.nullable().meta({
|
||||
description:
|
||||
"An account that can be contacted regarding inquiries or issues.",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Hints related to contacting a representative of the website.",
|
||||
}),
|
||||
rules: z.array(Rule).openapi({
|
||||
rules: z.array(Rule).meta({
|
||||
description: "An itemized list of rules for this website.",
|
||||
}),
|
||||
/* Versia Server API extension */
|
||||
sso: SSOConfig,
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Instance",
|
||||
},
|
||||
ref: "Instance",
|
||||
id: "Instance",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Id } from "./common.ts";
|
||||
|
||||
export const Marker = z
|
||||
.object({
|
||||
last_read_id: Id.openapi({
|
||||
last_read_id: Id.meta({
|
||||
description: "The ID of the most recently viewed entity.",
|
||||
example: "ead15c9d-8eda-4b2c-9546-ecbf851f001c",
|
||||
}),
|
||||
version: z.number().openapi({
|
||||
version: z.number().meta({
|
||||
description:
|
||||
"An incrementing counter, used for locking to prevent write conflicts.",
|
||||
example: 462,
|
||||
}),
|
||||
updated_at: z.string().datetime().openapi({
|
||||
updated_at: z.iso.datetime().meta({
|
||||
description: "The timestamp of when the marker was set.",
|
||||
example: "2025-01-12T13:11:00Z",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents the last read position within a user's timelines.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Marker",
|
||||
},
|
||||
ref: "Marker",
|
||||
id: "Marker",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Account } from "./account.ts";
|
||||
import { AccountWarning } from "./account-warning.ts";
|
||||
import { Id } from "./common.ts";
|
||||
|
|
@ -7,7 +7,7 @@ import { Status } from "./status.ts";
|
|||
|
||||
export const Notification = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "The ID of the notification in the database.",
|
||||
example: "6405f495-da55-4ad7-b5d6-9a773360fc07",
|
||||
}),
|
||||
|
|
@ -27,47 +27,46 @@ export const Notification = z
|
|||
"severed_relationships",
|
||||
"moderation_warning",
|
||||
])
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"The type of event that resulted in the notification.",
|
||||
example: "mention",
|
||||
}),
|
||||
group_key: z.string().openapi({
|
||||
group_key: z.string().meta({
|
||||
description:
|
||||
"Group key shared by similar notifications, to be used in the grouped notifications feature.",
|
||||
example: "ungrouped-34975861",
|
||||
}),
|
||||
created_at: z.string().datetime().openapi({
|
||||
created_at: z.iso.datetime().meta({
|
||||
description: "The timestamp of the notification.",
|
||||
example: "2025-01-12T13:11:00Z",
|
||||
}),
|
||||
account: Account.openapi({
|
||||
account: Account.meta({
|
||||
description:
|
||||
"The account that performed the action that generated the notification.",
|
||||
}),
|
||||
status: Status.optional().openapi({
|
||||
status: Status.optional().meta({
|
||||
description:
|
||||
"Status that was the object of the notification. Attached when type of the notification is favourite, reblog, status, mention, poll, or update.",
|
||||
}),
|
||||
report: Report.optional().openapi({
|
||||
report: Report.optional().meta({
|
||||
description:
|
||||
"Report that was the object of the notification. Attached when type of the notification is admin.report.",
|
||||
}),
|
||||
event: z.undefined().openapi({
|
||||
event: z.undefined().meta({
|
||||
description:
|
||||
"Versia Server does not sever relationships, so this field is always empty.",
|
||||
type: "null",
|
||||
}),
|
||||
moderation_warning: AccountWarning.optional().openapi({
|
||||
moderation_warning: AccountWarning.optional().meta({
|
||||
description:
|
||||
"Moderation warning that caused the notification. Attached when type of the notification is moderation_warning.",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents a notification of an event relevant to the user.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Notification",
|
||||
},
|
||||
ref: "Notification",
|
||||
id: "Notification",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Id } from "./common.ts";
|
||||
import { CustomEmoji } from "./emoji.ts";
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ export const PollOption = z
|
|||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The text value of the poll option.",
|
||||
example: "yes",
|
||||
externalDocs: {
|
||||
|
|
@ -20,7 +20,7 @@ export const PollOption = z
|
|||
.int()
|
||||
.nonnegative()
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"The total number of received votes for this option.",
|
||||
example: 6,
|
||||
|
|
@ -29,41 +29,40 @@ export const PollOption = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Poll/#Option",
|
||||
},
|
||||
ref: "PollOption",
|
||||
id: "PollOption",
|
||||
});
|
||||
|
||||
export const Poll = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "ID of the poll in the database.",
|
||||
example: "d87d230f-e401-4282-80c7-2044ab989662",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Poll/#id",
|
||||
},
|
||||
}),
|
||||
expires_at: z
|
||||
.string()
|
||||
expires_at: z.iso
|
||||
.datetime()
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "When the poll ends.",
|
||||
example: "2025-01-07T14:11:00.000Z",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Poll/#expires_at",
|
||||
},
|
||||
}),
|
||||
expired: z.boolean().openapi({
|
||||
expired: z.boolean().meta({
|
||||
description: "Is the poll currently expired?",
|
||||
example: false,
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Poll/#expired",
|
||||
},
|
||||
}),
|
||||
multiple: z.boolean().openapi({
|
||||
multiple: z.boolean().meta({
|
||||
description: "Does the poll allow multiple-choice answers?",
|
||||
example: false,
|
||||
externalDocs: {
|
||||
|
|
@ -74,7 +73,7 @@ export const Poll = z
|
|||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "How many votes have been received.",
|
||||
example: 6,
|
||||
externalDocs: {
|
||||
|
|
@ -86,7 +85,7 @@ export const Poll = z
|
|||
.int()
|
||||
.nonnegative()
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"How many unique accounts have voted on a multiple-choice poll.",
|
||||
example: 3,
|
||||
|
|
@ -94,13 +93,13 @@ export const Poll = z
|
|||
url: "https://docs.joinmastodon.org/entities/Poll/#voters_count",
|
||||
},
|
||||
}),
|
||||
options: z.array(PollOption).openapi({
|
||||
options: z.array(PollOption).meta({
|
||||
description: "Possible answers for the poll.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Poll/#options",
|
||||
},
|
||||
}),
|
||||
emojis: z.array(CustomEmoji).openapi({
|
||||
emojis: z.array(CustomEmoji).meta({
|
||||
description: "Custom emoji to be used for rendering poll options.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Poll/#emojis",
|
||||
|
|
@ -109,7 +108,7 @@ export const Poll = z
|
|||
voted: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"When called with a user token, has the authorized user voted?",
|
||||
example: true,
|
||||
|
|
@ -120,7 +119,7 @@ export const Poll = z
|
|||
own_votes: z
|
||||
.array(z.number().int())
|
||||
.optional()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"When called with a user token, which options has the authorized user chosen? Contains an array of index values for options.",
|
||||
example: [0],
|
||||
|
|
@ -129,10 +128,10 @@ export const Poll = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Represents a poll attached to a status.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Poll",
|
||||
},
|
||||
ref: "Poll",
|
||||
id: "Poll",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Source } from "./account.ts";
|
||||
|
||||
export const Preferences = z
|
||||
.object({
|
||||
"posting:default:visibility": Source.shape.privacy.openapi({
|
||||
"posting:default:visibility": Source.shape.privacy.meta({
|
||||
description: "Default visibility for new posts.",
|
||||
example: "public",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Preferences/#posting-default-visibility",
|
||||
},
|
||||
}),
|
||||
"posting:default:sensitive": Source.shape.sensitive.openapi({
|
||||
"posting:default:sensitive": Source.shape.sensitive.meta({
|
||||
description: "Default sensitivity flag for new posts.",
|
||||
example: false,
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Preferences/#posting-default-sensitive",
|
||||
},
|
||||
}),
|
||||
"posting:default:language": Source.shape.language.nullable().openapi({
|
||||
"posting:default:language": Source.shape.language.nullable().meta({
|
||||
description: "Default language for new posts.",
|
||||
example: null,
|
||||
externalDocs: {
|
||||
|
|
@ -26,7 +26,7 @@ export const Preferences = z
|
|||
}),
|
||||
"reading:expand:media": z
|
||||
.enum(["default", "show_all", "hide_all"])
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Whether media attachments should be automatically displayed or blurred/hidden.",
|
||||
example: "default",
|
||||
|
|
@ -34,7 +34,7 @@ export const Preferences = z
|
|||
url: "https://docs.joinmastodon.org/entities/Preferences/#reading-expand-media",
|
||||
},
|
||||
}),
|
||||
"reading:expand:spoilers": z.boolean().openapi({
|
||||
"reading:expand:spoilers": z.boolean().meta({
|
||||
description: "Whether CWs should be expanded by default.",
|
||||
example: false,
|
||||
externalDocs: {
|
||||
|
|
@ -42,10 +42,10 @@ export const Preferences = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Represents a user's preferences.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Preferences",
|
||||
},
|
||||
ref: "Preferences",
|
||||
id: "Preferences",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,19 +1,16 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const PrivacyPolicy = z
|
||||
.object({
|
||||
updated_at: z
|
||||
.string()
|
||||
.datetime()
|
||||
.openapi({
|
||||
description:
|
||||
"A timestamp of when the privacy policy was last updated.",
|
||||
example: "2025-01-12T13:11:00Z",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PrivacyPolicy/#updated_at",
|
||||
},
|
||||
}),
|
||||
content: z.string().openapi({
|
||||
updated_at: z.iso.datetime().meta({
|
||||
description:
|
||||
"A timestamp of when the privacy policy was last updated.",
|
||||
example: "2025-01-12T13:11:00Z",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PrivacyPolicy/#updated_at",
|
||||
},
|
||||
}),
|
||||
content: z.string().meta({
|
||||
description: "The rendered HTML content of the privacy policy.",
|
||||
example: "<p><h1>Privacy Policy</h1><p>None, good luck.</p></p>",
|
||||
externalDocs: {
|
||||
|
|
@ -21,10 +18,10 @@ export const PrivacyPolicy = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Represents the privacy policy of the instance.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/PrivacyPolicy",
|
||||
},
|
||||
ref: "PrivacyPolicy",
|
||||
id: "PrivacyPolicy",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,64 +1,64 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Id } from "./common.ts";
|
||||
|
||||
export const WebPushSubscription = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
example: "24eb1891-accc-43b4-b213-478e37d525b4",
|
||||
description: "The ID of the Web Push subscription in the database.",
|
||||
}),
|
||||
endpoint: z.string().url().openapi({
|
||||
endpoint: z.url().meta({
|
||||
example: "https://yourdomain.example/listener",
|
||||
description: "Where push alerts will be sent to.",
|
||||
}),
|
||||
alerts: z
|
||||
.object({
|
||||
mention: z.boolean().optional().openapi({
|
||||
mention: z.boolean().optional().meta({
|
||||
example: true,
|
||||
description: "Receive mention notifications?",
|
||||
}),
|
||||
favourite: z.boolean().optional().openapi({
|
||||
favourite: z.boolean().optional().meta({
|
||||
example: true,
|
||||
description: "Receive favourite notifications?",
|
||||
}),
|
||||
reblog: z.boolean().optional().openapi({
|
||||
reblog: z.boolean().optional().meta({
|
||||
example: true,
|
||||
description: "Receive reblog notifications?",
|
||||
}),
|
||||
follow: z.boolean().optional().openapi({
|
||||
follow: z.boolean().optional().meta({
|
||||
example: true,
|
||||
description: "Receive follow notifications?",
|
||||
}),
|
||||
poll: z.boolean().optional().openapi({
|
||||
poll: z.boolean().optional().meta({
|
||||
example: false,
|
||||
description: "Receive poll notifications?",
|
||||
}),
|
||||
follow_request: z.boolean().optional().openapi({
|
||||
follow_request: z.boolean().optional().meta({
|
||||
example: false,
|
||||
description: "Receive follow request notifications?",
|
||||
}),
|
||||
status: z.boolean().optional().openapi({
|
||||
status: z.boolean().optional().meta({
|
||||
example: false,
|
||||
description:
|
||||
"Receive new subscribed account notifications?",
|
||||
}),
|
||||
update: z.boolean().optional().openapi({
|
||||
update: z.boolean().optional().meta({
|
||||
example: false,
|
||||
description: "Receive status edited notifications?",
|
||||
}),
|
||||
"admin.sign_up": z.boolean().optional().openapi({
|
||||
"admin.sign_up": z.boolean().optional().meta({
|
||||
example: false,
|
||||
description:
|
||||
"Receive new user signup notifications? Must have a role with the appropriate permissions.",
|
||||
}),
|
||||
"admin.report": z.boolean().optional().openapi({
|
||||
"admin.report": z.boolean().optional().meta({
|
||||
example: false,
|
||||
description:
|
||||
"Receive new report notifications? Must have a role with the appropriate permissions.",
|
||||
}),
|
||||
})
|
||||
.default({})
|
||||
.openapi({
|
||||
.meta({
|
||||
example: {
|
||||
mention: true,
|
||||
favourite: true,
|
||||
|
|
@ -74,62 +74,57 @@ export const WebPushSubscription = z
|
|||
description:
|
||||
"Which alerts should be delivered to the endpoint.",
|
||||
}),
|
||||
server_key: z.string().openapi({
|
||||
server_key: z.string().meta({
|
||||
example:
|
||||
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
|
||||
description: "The streaming server’s VAPID key.",
|
||||
}),
|
||||
})
|
||||
.openapi({ ref: "WebPushSubscription" });
|
||||
.meta({ id: "WebPushSubscription" });
|
||||
|
||||
export const WebPushSubscriptionInput = z
|
||||
.object({
|
||||
subscription: z.object({
|
||||
endpoint: z.string().url().openapi({
|
||||
example: "https://yourdomain.example/listener",
|
||||
description: "Where push alerts will be sent to.",
|
||||
}),
|
||||
keys: z
|
||||
.object({
|
||||
p256dh: z.string().base64url().openapi({
|
||||
description:
|
||||
"User agent public key. Base64url encoded string of a public key from a ECDH keypair using the prime256v1 curve.",
|
||||
example:
|
||||
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoKCJeHCy69ywHcb3dAR/T8Sud5ljSFHJkuiR6it1ycqAjGTe5F1oZ0ef5QiMX/zdQ+d4jSKiO7RztIz+o/eGuQ==",
|
||||
}),
|
||||
auth: z.string().base64url().openapi({
|
||||
description:
|
||||
"Auth secret. Base64url encoded string of 16 bytes of random data.",
|
||||
example: "u67u09PXZW4ncK9l9mAXkA==",
|
||||
}),
|
||||
})
|
||||
.strict(),
|
||||
export const WebPushSubscriptionInput = z.strictObject({
|
||||
subscription: z.object({
|
||||
endpoint: z.url().meta({
|
||||
example: "https://yourdomain.example/listener",
|
||||
description: "Where push alerts will be sent to.",
|
||||
}),
|
||||
policy: z
|
||||
.enum(["all", "followed", "follower", "none"])
|
||||
.default("all")
|
||||
.openapi({
|
||||
keys: z.strictObject({
|
||||
p256dh: z.base64url().meta({
|
||||
description:
|
||||
"Specify whether to receive push notifications from all, followed, follower, or none users.",
|
||||
"User agent public key. Base64url encoded string of a public key from a ECDH keypair using the prime256v1 curve.",
|
||||
example:
|
||||
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoKCJeHCy69ywHcb3dAR/T8Sud5ljSFHJkuiR6it1ycqAjGTe5F1oZ0ef5QiMX/zdQ+d4jSKiO7RztIz+o/eGuQ==",
|
||||
}),
|
||||
data: z
|
||||
.object({
|
||||
alerts: WebPushSubscription.shape.alerts,
|
||||
})
|
||||
.strict()
|
||||
.default({
|
||||
alerts: {
|
||||
mention: false,
|
||||
favourite: false,
|
||||
reblog: false,
|
||||
follow: false,
|
||||
poll: false,
|
||||
follow_request: false,
|
||||
status: false,
|
||||
update: false,
|
||||
"admin.sign_up": false,
|
||||
"admin.report": false,
|
||||
},
|
||||
auth: z.base64url().meta({
|
||||
description:
|
||||
"Auth secret. Base64url encoded string of 16 bytes of random data.",
|
||||
example: "u67u09PXZW4ncK9l9mAXkA==",
|
||||
}),
|
||||
})
|
||||
.strict();
|
||||
}),
|
||||
}),
|
||||
policy: z
|
||||
.enum(["all", "followed", "follower", "none"])
|
||||
.default("all")
|
||||
.meta({
|
||||
description:
|
||||
"Specify whether to receive push notifications from all, followed, follower, or none users.",
|
||||
}),
|
||||
data: z
|
||||
.strictObject({
|
||||
alerts: WebPushSubscription.shape.alerts,
|
||||
})
|
||||
.default({
|
||||
alerts: {
|
||||
mention: false,
|
||||
favourite: false,
|
||||
reblog: false,
|
||||
follow: false,
|
||||
poll: false,
|
||||
follow_request: false,
|
||||
status: false,
|
||||
update: false,
|
||||
"admin.sign_up": false,
|
||||
"admin.report": false,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,75 +1,75 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Id, iso631 } from "./common.ts";
|
||||
|
||||
export const Relationship = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "The account ID.",
|
||||
example: "51f34c31-c8c6-4dc2-9df1-3704fcdde9b6",
|
||||
}),
|
||||
following: z.boolean().openapi({
|
||||
following: z.boolean().meta({
|
||||
description: "Are you following this user?",
|
||||
example: true,
|
||||
}),
|
||||
showing_reblogs: z.boolean().openapi({
|
||||
showing_reblogs: z.boolean().meta({
|
||||
description:
|
||||
"Are you receiving this user’s boosts in your home timeline?",
|
||||
example: true,
|
||||
}),
|
||||
notifying: z.boolean().openapi({
|
||||
notifying: z.boolean().meta({
|
||||
description: "Have you enabled notifications for this user?",
|
||||
example: false,
|
||||
}),
|
||||
languages: z.array(iso631).openapi({
|
||||
languages: z.array(iso631).meta({
|
||||
description: "Which languages are you following from this user?",
|
||||
example: ["en"],
|
||||
}),
|
||||
followed_by: z.boolean().openapi({
|
||||
followed_by: z.boolean().meta({
|
||||
description: "Are you followed by this user?",
|
||||
example: true,
|
||||
}),
|
||||
blocking: z.boolean().openapi({
|
||||
blocking: z.boolean().meta({
|
||||
description: "Are you blocking this user?",
|
||||
example: false,
|
||||
}),
|
||||
blocked_by: z.boolean().openapi({
|
||||
blocked_by: z.boolean().meta({
|
||||
description: "Is this user blocking you?",
|
||||
example: false,
|
||||
}),
|
||||
muting: z.boolean().openapi({
|
||||
muting: z.boolean().meta({
|
||||
description: "Are you muting this user?",
|
||||
example: false,
|
||||
}),
|
||||
muting_notifications: z.boolean().openapi({
|
||||
muting_notifications: z.boolean().meta({
|
||||
description: "Are you muting notifications from this user?",
|
||||
example: false,
|
||||
}),
|
||||
requested: z.boolean().openapi({
|
||||
requested: z.boolean().meta({
|
||||
description: "Do you have a pending follow request for this user?",
|
||||
example: false,
|
||||
}),
|
||||
requested_by: z.boolean().openapi({
|
||||
requested_by: z.boolean().meta({
|
||||
description: "Has this user requested to follow you?",
|
||||
example: false,
|
||||
}),
|
||||
domain_blocking: z.boolean().openapi({
|
||||
domain_blocking: z.boolean().meta({
|
||||
description: "Are you blocking this user’s domain?",
|
||||
example: false,
|
||||
}),
|
||||
endorsed: z.boolean().openapi({
|
||||
endorsed: z.boolean().meta({
|
||||
description: "Are you featuring this user on your profile?",
|
||||
example: false,
|
||||
}),
|
||||
note: z.string().min(0).max(5000).trim().openapi({
|
||||
note: z.string().min(0).max(5000).trim().meta({
|
||||
description: "This user’s profile bio",
|
||||
example: "they also like Kerbal Space Program",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents the relationship between accounts, such as following / blocking / muting / etc.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Relationship",
|
||||
},
|
||||
ref: "Relationship",
|
||||
id: "Relationship",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,60 +1,60 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Account } from "./account.ts";
|
||||
import { Id } from "./common.ts";
|
||||
|
||||
export const Report = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "The ID of the report in the database.",
|
||||
example: "9b0cd757-324b-4ea6-beab-f6226e138886",
|
||||
}),
|
||||
action_taken: z.boolean().openapi({
|
||||
action_taken: z.boolean().meta({
|
||||
description: "Whether an action was taken yet.",
|
||||
example: false,
|
||||
}),
|
||||
action_taken_at: z.string().datetime().nullable().openapi({
|
||||
action_taken_at: z.iso.datetime().nullable().meta({
|
||||
description: "When an action was taken against the report.",
|
||||
example: null,
|
||||
}),
|
||||
category: z.enum(["spam", "violation", "other"]).openapi({
|
||||
category: z.enum(["spam", "violation", "other"]).meta({
|
||||
description:
|
||||
"The generic reason for the report. 'spam' = Unwanted or repetitive content, 'violation' = A specific rule was violated, 'other' = Some other reason.",
|
||||
example: "spam",
|
||||
}),
|
||||
comment: z.string().openapi({
|
||||
comment: z.string().meta({
|
||||
description: "The reason for the report.",
|
||||
example: "Spam account",
|
||||
}),
|
||||
forwarded: z.boolean().openapi({
|
||||
forwarded: z.boolean().meta({
|
||||
description: "Whether the report was forwarded to a remote domain.",
|
||||
example: false,
|
||||
}),
|
||||
created_at: z.string().datetime().openapi({
|
||||
created_at: z.iso.datetime().meta({
|
||||
description: "When the report was created.",
|
||||
example: "2024-12-31T23:59:59.999Z",
|
||||
}),
|
||||
status_ids: z
|
||||
.array(Id)
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"IDs of statuses that have been attached to this report for additional context.",
|
||||
example: ["1abf027c-af03-46ff-8d17-9ee799a17ca7"],
|
||||
}),
|
||||
rule_ids: z.array(z.string()).nullable().openapi({
|
||||
rule_ids: z.array(z.string()).nullable().meta({
|
||||
description:
|
||||
"IDs of the rules that have been cited as a violation by this report.",
|
||||
example: null,
|
||||
}),
|
||||
target_account: Account.openapi({
|
||||
target_account: Account.meta({
|
||||
description: "The account that was reported.",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Reports filed against users and/or statuses, to be taken action on by moderators.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Report",
|
||||
},
|
||||
ref: "Report",
|
||||
id: "Report",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const Rule = z
|
||||
.object({
|
||||
id: z.string().openapi({
|
||||
id: z.string().meta({
|
||||
description: "The identifier for the rule.",
|
||||
example: "1",
|
||||
}),
|
||||
text: z.string().openapi({
|
||||
text: z.string().meta({
|
||||
description: "The rule to be followed.",
|
||||
example: "Do not spam pictures of skibidi toilet.",
|
||||
}),
|
||||
hint: z.string().optional().openapi({
|
||||
hint: z.string().optional().meta({
|
||||
description: "Longer-form description of the rule.",
|
||||
example: "Please, we beg you.",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Represents a rule that server users should follow.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Rule",
|
||||
},
|
||||
ref: "Rule",
|
||||
id: "Rule",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Account } from "./account.ts";
|
||||
import { Status } from "./status.ts";
|
||||
import { Tag } from "./tag.ts";
|
||||
|
||||
export const Search = z
|
||||
.object({
|
||||
accounts: z.array(Account).openapi({
|
||||
accounts: z.array(Account).meta({
|
||||
description: "Accounts which match the given query",
|
||||
}),
|
||||
statuses: z.array(Status).openapi({
|
||||
statuses: z.array(Status).meta({
|
||||
description: "Statuses which match the given query",
|
||||
}),
|
||||
hashtags: z.array(Tag).openapi({
|
||||
hashtags: z.array(Tag).meta({
|
||||
description: "Hashtags which match the given query",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Represents the results of a search.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Search",
|
||||
},
|
||||
ref: "Search",
|
||||
id: "Search",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Account } from "./account.ts";
|
||||
import { Attachment } from "./attachment.ts";
|
||||
import { PreviewCard } from "./card.ts";
|
||||
|
|
@ -11,28 +11,28 @@ import { NoteReaction } from "./versia.ts";
|
|||
|
||||
export const Mention = z
|
||||
.object({
|
||||
id: Account.shape.id.openapi({
|
||||
id: Account.shape.id.meta({
|
||||
description: "The account ID of the mentioned user.",
|
||||
example: "b9dcb548-bd4d-42af-8b48-3693e6d298e6",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#Mention-id",
|
||||
},
|
||||
}),
|
||||
username: Account.shape.username.openapi({
|
||||
username: Account.shape.username.meta({
|
||||
description: "The username of the mentioned user.",
|
||||
example: "lexi",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#Mention-username",
|
||||
},
|
||||
}),
|
||||
url: Account.shape.url.openapi({
|
||||
url: Account.shape.url.meta({
|
||||
description: "The location of the mentioned user’s profile.",
|
||||
example: "https://beta.versia.social/@lexi",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#Mention-url",
|
||||
},
|
||||
}),
|
||||
acct: Account.shape.acct.openapi({
|
||||
acct: Account.shape.acct.meta({
|
||||
description:
|
||||
"The webfinger acct: URI of the mentioned user. Equivalent to username for local users, or username@domain for remote users.",
|
||||
example: "lexi@beta.versia.social",
|
||||
|
|
@ -41,64 +41,59 @@ export const Mention = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#Mention",
|
||||
},
|
||||
ref: "Mention",
|
||||
id: "Mention",
|
||||
});
|
||||
|
||||
export const StatusSource = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "ID of the status in the database.",
|
||||
example: "c7db92a4-e472-4e94-a115-7411ee934ba1",
|
||||
}),
|
||||
text: z.string().trim().openapi({
|
||||
text: z.string().trim().meta({
|
||||
description: "The plain text used to compose the status.",
|
||||
example: "this is a status that will be edited",
|
||||
}),
|
||||
// min(0) because some masto-fe clients send empty spoiler_text
|
||||
// when they don't want to set it.
|
||||
spoiler_text: z.string().trim().min(0).max(1024).openapi({
|
||||
spoiler_text: z.string().trim().min(0).max(1024).meta({
|
||||
description:
|
||||
"The plain text used to compose the status’s subject or content warning.",
|
||||
example: "",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/StatusSource",
|
||||
},
|
||||
ref: "StatusSource",
|
||||
id: "StatusSource",
|
||||
});
|
||||
|
||||
// Because Status has some recursive references, we need to define it like this
|
||||
const BaseStatus = z
|
||||
export const Status = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "ID of the status in the database.",
|
||||
example: "2de861d3-a3dd-42ee-ba38-2c7d3f4af588",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#id",
|
||||
},
|
||||
}),
|
||||
uri: z
|
||||
.string()
|
||||
.url()
|
||||
.openapi({
|
||||
description: "URI of the status used for federation.",
|
||||
example:
|
||||
"https://beta.versia.social/@lexi/2de861d3-a3dd-42ee-ba38-2c7d3f4af588",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#uri",
|
||||
},
|
||||
}),
|
||||
uri: z.url().meta({
|
||||
description: "URI of the status used for federation.",
|
||||
example:
|
||||
"https://beta.versia.social/@lexi/2de861d3-a3dd-42ee-ba38-2c7d3f4af588",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#uri",
|
||||
},
|
||||
}),
|
||||
url: z
|
||||
.string()
|
||||
.url()
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "A link to the status’s HTML representation.",
|
||||
example:
|
||||
"https://beta.versia.social/@lexi/2de861d3-a3dd-42ee-ba38-2c7d3f4af588",
|
||||
|
|
@ -106,20 +101,20 @@ const BaseStatus = z
|
|||
url: "https://docs.joinmastodon.org/entities/Status/#url",
|
||||
},
|
||||
}),
|
||||
account: Account.openapi({
|
||||
account: Account.meta({
|
||||
description: "The account that authored this status.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#account",
|
||||
},
|
||||
}),
|
||||
in_reply_to_id: Id.nullable().openapi({
|
||||
in_reply_to_id: Id.nullable().meta({
|
||||
description: "ID of the status being replied to.",
|
||||
example: "c41c9fe9-919a-4d35-a921-d3e79a5c95f8",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#in_reply_to_id",
|
||||
},
|
||||
}),
|
||||
in_reply_to_account_id: Account.shape.id.nullable().openapi({
|
||||
in_reply_to_account_id: Account.shape.id.nullable().meta({
|
||||
description:
|
||||
"ID of the account that authored the status being replied to.",
|
||||
example: "7b9b3ec6-1013-4cc6-8902-94ad00cf2ccc",
|
||||
|
|
@ -128,35 +123,31 @@ const BaseStatus = z
|
|||
},
|
||||
}),
|
||||
|
||||
content: z.string().openapi({
|
||||
content: z.string().meta({
|
||||
description: "HTML-encoded status content.",
|
||||
example: "<p>hello world</p>",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#content",
|
||||
},
|
||||
}),
|
||||
created_at: z
|
||||
.string()
|
||||
.datetime()
|
||||
.openapi({
|
||||
description: "The date when this status was created.",
|
||||
example: "2025-01-07T14:11:00.000Z",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#created_at",
|
||||
},
|
||||
}),
|
||||
edited_at: z
|
||||
.string()
|
||||
created_at: z.iso.datetime().meta({
|
||||
description: "The date when this status was created.",
|
||||
example: "2025-01-07T14:11:00.000Z",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#created_at",
|
||||
},
|
||||
}),
|
||||
edited_at: z.iso
|
||||
.datetime()
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Timestamp of when the status was last edited.",
|
||||
example: "2025-01-07T14:11:00.000Z",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#edited_at",
|
||||
},
|
||||
}),
|
||||
emojis: z.array(CustomEmoji).openapi({
|
||||
emojis: z.array(CustomEmoji).meta({
|
||||
description:
|
||||
"Custom emoji to be used when rendering status content.",
|
||||
externalDocs: {
|
||||
|
|
@ -167,7 +158,7 @@ const BaseStatus = z
|
|||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "How many replies this status has received.",
|
||||
example: 1,
|
||||
externalDocs: {
|
||||
|
|
@ -178,7 +169,7 @@ const BaseStatus = z
|
|||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "How many boosts this status has received.",
|
||||
example: 6,
|
||||
externalDocs: {
|
||||
|
|
@ -189,14 +180,14 @@ const BaseStatus = z
|
|||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "How many favourites this status has received.",
|
||||
example: 11,
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#favourites_count",
|
||||
},
|
||||
}),
|
||||
reblogged: zBoolean.optional().openapi({
|
||||
reblogged: zBoolean.optional().meta({
|
||||
description:
|
||||
"If the current token has an authorized user: Have you boosted this status?",
|
||||
example: false,
|
||||
|
|
@ -204,7 +195,7 @@ const BaseStatus = z
|
|||
url: "https://docs.joinmastodon.org/entities/Status/#reblogged",
|
||||
},
|
||||
}),
|
||||
favourited: zBoolean.optional().openapi({
|
||||
favourited: zBoolean.optional().meta({
|
||||
description:
|
||||
"If the current token has an authorized user: Have you favourited this status?",
|
||||
example: true,
|
||||
|
|
@ -212,7 +203,7 @@ const BaseStatus = z
|
|||
url: "https://docs.joinmastodon.org/entities/Status/#favourited",
|
||||
},
|
||||
}),
|
||||
muted: zBoolean.optional().openapi({
|
||||
muted: zBoolean.optional().meta({
|
||||
description:
|
||||
"If the current token has an authorized user: Have you muted notifications for this status’s conversation?",
|
||||
example: false,
|
||||
|
|
@ -220,14 +211,14 @@ const BaseStatus = z
|
|||
url: "https://docs.joinmastodon.org/entities/Status/#muted",
|
||||
},
|
||||
}),
|
||||
sensitive: zBoolean.openapi({
|
||||
sensitive: zBoolean.meta({
|
||||
description: "Is this status marked as sensitive content?",
|
||||
example: false,
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#sensitive",
|
||||
},
|
||||
}),
|
||||
spoiler_text: z.string().openapi({
|
||||
spoiler_text: z.string().meta({
|
||||
description:
|
||||
"Subject or summary line, below which status content is collapsed until expanded.",
|
||||
example: "lewd text",
|
||||
|
|
@ -235,41 +226,39 @@ const BaseStatus = z
|
|||
url: "https://docs.joinmastodon.org/entities/Status/#spoiler_text",
|
||||
},
|
||||
}),
|
||||
visibility: z
|
||||
.enum(["public", "unlisted", "private", "direct"])
|
||||
.openapi({
|
||||
description: "Visibility of this status.",
|
||||
example: "public",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#visibility",
|
||||
},
|
||||
}),
|
||||
media_attachments: z.array(Attachment).openapi({
|
||||
visibility: z.enum(["public", "unlisted", "private", "direct"]).meta({
|
||||
description: "Visibility of this status.",
|
||||
example: "public",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#visibility",
|
||||
},
|
||||
}),
|
||||
media_attachments: z.array(Attachment).meta({
|
||||
description: "Media that is attached to this status.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#media_attachments",
|
||||
},
|
||||
}),
|
||||
mentions: z.array(Mention).openapi({
|
||||
mentions: z.array(Mention).meta({
|
||||
description: "Mentions of users within the status content.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#mentions",
|
||||
},
|
||||
}),
|
||||
tags: z.array(Tag).openapi({
|
||||
tags: z.array(Tag).meta({
|
||||
description: "Hashtags used within the status content.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#tags",
|
||||
},
|
||||
}),
|
||||
card: PreviewCard.nullable().openapi({
|
||||
card: PreviewCard.nullable().meta({
|
||||
description:
|
||||
"Preview card for links included within status content.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#card",
|
||||
},
|
||||
}),
|
||||
poll: Poll.nullable().openapi({
|
||||
poll: Poll.nullable().meta({
|
||||
description: "The poll attached to the status.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#poll",
|
||||
|
|
@ -277,7 +266,7 @@ const BaseStatus = z
|
|||
}),
|
||||
application: z
|
||||
.object({
|
||||
name: z.string().openapi({
|
||||
name: z.string().meta({
|
||||
description:
|
||||
"The name of the application that posted this status.",
|
||||
externalDocs: {
|
||||
|
|
@ -285,10 +274,9 @@ const BaseStatus = z
|
|||
},
|
||||
}),
|
||||
website: z
|
||||
.string()
|
||||
.url()
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"The website associated with the application that posted this status.",
|
||||
externalDocs: {
|
||||
|
|
@ -297,30 +285,41 @@ const BaseStatus = z
|
|||
}),
|
||||
})
|
||||
.optional()
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The application used to post this status.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#application",
|
||||
},
|
||||
}),
|
||||
language: iso631.nullable().openapi({
|
||||
language: iso631.nullable().meta({
|
||||
description: "Primary language of this status.",
|
||||
example: "en",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#language",
|
||||
},
|
||||
}),
|
||||
get reblog() {
|
||||
return Status.nullable().meta({
|
||||
description: "The status being reblogged.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#reblog",
|
||||
},
|
||||
});
|
||||
},
|
||||
get quote() {
|
||||
return Status.nullable();
|
||||
},
|
||||
text: z
|
||||
.string()
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Plain-text source of a status. Returned instead of content when status is deleted, so the user may redraft from the source text without the client having to reverse-engineer the original text from the HTML content.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#text",
|
||||
},
|
||||
}),
|
||||
pinned: zBoolean.optional().openapi({
|
||||
pinned: zBoolean.optional().meta({
|
||||
description:
|
||||
"If the current token has an authorized user: Have you pinned this status? Only appears if the status is pinnable.",
|
||||
example: true,
|
||||
|
|
@ -328,8 +327,8 @@ const BaseStatus = z
|
|||
url: "https://docs.joinmastodon.org/entities/Status/#pinned",
|
||||
},
|
||||
}),
|
||||
reactions: z.array(NoteReaction).openapi({}),
|
||||
bookmarked: zBoolean.optional().openapi({
|
||||
reactions: z.array(NoteReaction).meta({}),
|
||||
bookmarked: zBoolean.optional().meta({
|
||||
description:
|
||||
"If the current token has an authorized user: Have you bookmarked this status?",
|
||||
example: false,
|
||||
|
|
@ -340,7 +339,7 @@ const BaseStatus = z
|
|||
filtered: z
|
||||
.array(FilterResult)
|
||||
.optional()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"If the current token has an authorized user: The filter and keywords that matched this status.",
|
||||
externalDocs: {
|
||||
|
|
@ -348,35 +347,23 @@ const BaseStatus = z
|
|||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
ref: "BaseStatus",
|
||||
.meta({
|
||||
id: "Status",
|
||||
});
|
||||
|
||||
export const Status = BaseStatus.extend({
|
||||
reblog: BaseStatus.nullable().openapi({
|
||||
description: "The status being reblogged.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#reblog",
|
||||
},
|
||||
}),
|
||||
quote: BaseStatus.nullable(),
|
||||
}).openapi({
|
||||
ref: "Status",
|
||||
});
|
||||
|
||||
export const ScheduledStatus = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "ID of the scheduled status in the database.",
|
||||
example: "2de861d3-a3dd-42ee-ba38-2c7d3f4af588",
|
||||
}),
|
||||
scheduled_at: z.string().datetime().openapi({
|
||||
scheduled_at: z.iso.datetime().meta({
|
||||
description: "When the status will be scheduled.",
|
||||
example: "2025-01-07T14:11:00.000Z",
|
||||
}),
|
||||
media_attachments: Status.shape.media_attachments,
|
||||
params: z.object({
|
||||
text: z.string().openapi({
|
||||
text: z.string().meta({
|
||||
description: "Text to be used as status content.",
|
||||
example: "Hello, world!",
|
||||
}),
|
||||
|
|
@ -384,7 +371,7 @@ export const ScheduledStatus = z
|
|||
media_ids: z
|
||||
.array(Id)
|
||||
.nullable()
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"IDs of the MediaAttachments that will be attached to the status.",
|
||||
example: ["1234567890", "1234567891"],
|
||||
|
|
@ -394,22 +381,22 @@ export const ScheduledStatus = z
|
|||
visibility: Status.shape.visibility,
|
||||
in_reply_to_id: Status.shape.in_reply_to_id,
|
||||
/** Versia Server API Extension */
|
||||
quote_id: z.string().openapi({
|
||||
quote_id: z.string().meta({
|
||||
description: "ID of the status being quoted.",
|
||||
example: "c5d62a13-f340-4e7d-8942-7fd14be688dc",
|
||||
}),
|
||||
language: Status.shape.language,
|
||||
scheduled_at: z.null().openapi({
|
||||
scheduled_at: z.null().meta({
|
||||
description:
|
||||
"When the status will be scheduled. This will be null because the status is only scheduled once.",
|
||||
example: null,
|
||||
}),
|
||||
idempotency: z.string().nullable().openapi({
|
||||
idempotency: z.string().nullable().meta({
|
||||
description: "Idempotency key to prevent duplicate statuses.",
|
||||
example: "1234567890",
|
||||
}),
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
ref: "ScheduledStatus",
|
||||
.meta({
|
||||
id: "ScheduledStatus",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const Tag = z
|
||||
.object({
|
||||
|
|
@ -6,27 +6,24 @@ export const Tag = z
|
|||
.string()
|
||||
.min(1)
|
||||
.max(128)
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "The value of the hashtag after the # sign.",
|
||||
example: "versia",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#Tag-name",
|
||||
},
|
||||
}),
|
||||
url: z
|
||||
.string()
|
||||
.url()
|
||||
.openapi({
|
||||
description: "A link to the hashtag on the instance.",
|
||||
example: "https://beta.versia.social/tags/versia",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#Tag-url",
|
||||
},
|
||||
}),
|
||||
url: z.url().meta({
|
||||
description: "A link to the hashtag on the instance.",
|
||||
example: "https://beta.versia.social/tags/versia",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#Tag-url",
|
||||
},
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Status/#Tag",
|
||||
},
|
||||
ref: "Tag",
|
||||
id: "Tag",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,30 +1,30 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const Token = z
|
||||
.object({
|
||||
access_token: z.string().openapi({
|
||||
access_token: z.string().meta({
|
||||
description: "An OAuth token to be used for authorization.",
|
||||
example: "ZA-Yj3aBD8U8Cm7lKUp-lm9O9BmDgdhHzDeqsY8tlL0",
|
||||
}),
|
||||
token_type: z.string().openapi({
|
||||
token_type: z.string().meta({
|
||||
description: "The OAuth token type. Versia uses Bearer tokens.",
|
||||
example: "Bearer",
|
||||
}),
|
||||
scope: z.string().openapi({
|
||||
scope: z.string().meta({
|
||||
description:
|
||||
"The OAuth scopes granted by this token, space-separated.",
|
||||
example: "read write follow push",
|
||||
}),
|
||||
created_at: z.number().nonnegative().openapi({
|
||||
created_at: z.number().nonnegative().meta({
|
||||
description: "When the token was generated. UNIX timestamp.",
|
||||
example: 1573979017,
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Represents an OAuth token used for authenticating with the API and performing actions.",
|
||||
externalDocs: {
|
||||
url: "https://docs.joinmastodon.org/entities/Token",
|
||||
},
|
||||
ref: "Token",
|
||||
id: "Token",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const TermsOfService = z
|
||||
.object({
|
||||
updated_at: z.string().datetime().openapi({
|
||||
updated_at: z.iso.datetime().meta({
|
||||
description: "A timestamp of when the ToS was last updated.",
|
||||
example: "2025-01-12T13:11:00Z",
|
||||
}),
|
||||
content: z.string().openapi({
|
||||
content: z.string().meta({
|
||||
description: "The rendered HTML content of the ToS.",
|
||||
example: "<p><h1>ToS</h1><p>None, have fun.</p></p>",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Represents the ToS of the instance.",
|
||||
ref: "TermsOfService",
|
||||
id: "TermsOfService",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,86 +1,78 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { Id } from "./common.ts";
|
||||
import { RolePermission } from "./permissions.ts";
|
||||
|
||||
/* Versia Server API extension */
|
||||
export const Role = z
|
||||
.object({
|
||||
id: Id.openapi({
|
||||
id: Id.meta({
|
||||
description: "The role ID in the database.",
|
||||
example: "b4a7e0f0-8f6a-479b-910b-9265c070d5bd",
|
||||
}),
|
||||
name: z.string().min(1).max(128).trim().openapi({
|
||||
name: z.string().min(1).max(128).trim().meta({
|
||||
description: "The name of the role.",
|
||||
example: "Moderator",
|
||||
}),
|
||||
permissions: z
|
||||
.array(z.nativeEnum(RolePermission))
|
||||
.transform(
|
||||
// Deduplicate permissions
|
||||
(permissions) => Array.from(new Set(permissions)),
|
||||
)
|
||||
.default([])
|
||||
.openapi({
|
||||
description: "The permissions granted to the role.",
|
||||
example: [
|
||||
RolePermission.ManageEmojis,
|
||||
RolePermission.ManageAccounts,
|
||||
],
|
||||
type: "array",
|
||||
}),
|
||||
priority: z.number().int().default(0).openapi({
|
||||
permissions: z.array(z.enum(RolePermission)).meta({
|
||||
description: "The permissions granted to the role.",
|
||||
example: [
|
||||
RolePermission.ManageEmojis,
|
||||
RolePermission.ManageAccounts,
|
||||
],
|
||||
}),
|
||||
priority: z.number().int().meta({
|
||||
description:
|
||||
"Role priority. Higher priority roles allow overriding lower priority roles.",
|
||||
example: 100,
|
||||
}),
|
||||
description: z.string().min(0).max(1024).trim().optional().openapi({
|
||||
description: z.string().min(0).max(1024).trim().optional().meta({
|
||||
description: "Short role description.",
|
||||
example: "Allows managing emojis and accounts.",
|
||||
}),
|
||||
visible: z.boolean().default(true).openapi({
|
||||
visible: z.boolean().default(true).meta({
|
||||
description: "Whether the role should be shown in the UI.",
|
||||
}),
|
||||
icon: z.string().url().optional().openapi({
|
||||
icon: z.url().optional().meta({
|
||||
description: "URL to the role icon.",
|
||||
example: "https://example.com/role-icon.png",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"Information about a role in the system, as well as its permissions.",
|
||||
ref: "Role",
|
||||
id: "Role",
|
||||
});
|
||||
|
||||
/* Versia Server API extension */
|
||||
export const NoteReaction = z
|
||||
.object({
|
||||
name: z.string().min(1).trim().openapi({
|
||||
name: z.string().min(1).trim().meta({
|
||||
description: "Custom Emoji shortcode or Unicode emoji.",
|
||||
example: "blobfox_coffee",
|
||||
}),
|
||||
count: z.number().int().nonnegative().openapi({
|
||||
count: z.number().int().nonnegative().meta({
|
||||
description: "Number of users who reacted with this emoji.",
|
||||
example: 5,
|
||||
}),
|
||||
remote: z.boolean().openapi({
|
||||
remote: z.boolean().meta({
|
||||
description:
|
||||
"Whether this reaction is from a remote instance (federated).",
|
||||
example: false,
|
||||
}),
|
||||
me: z.boolean().optional().openapi({
|
||||
me: z.boolean().optional().meta({
|
||||
description:
|
||||
"Whether the current authenticated user reacted with this emoji.",
|
||||
example: true,
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "Information about a reaction to a note.",
|
||||
ref: "NoteReaction",
|
||||
id: "NoteReaction",
|
||||
});
|
||||
|
||||
/* Versia Server API extension */
|
||||
export const NoteReactionWithAccounts = NoteReaction.extend({
|
||||
account_ids: z.array(Id).openapi({
|
||||
account_ids: z.array(Id).meta({
|
||||
description: "Array of user IDs who reacted with this emoji.",
|
||||
example: [
|
||||
"1d0185bc-d949-4ff5-8a15-1d691b256489",
|
||||
|
|
@ -88,14 +80,14 @@ export const NoteReactionWithAccounts = NoteReaction.extend({
|
|||
"1f0c4eb9-a742-4c82-96c9-697a39831cd1",
|
||||
],
|
||||
}),
|
||||
}).openapi({
|
||||
}).meta({
|
||||
description: "Information about a reaction to a note with account IDs.",
|
||||
ref: "NoteReactionWithAccounts",
|
||||
id: "NoteReactionWithAccounts",
|
||||
});
|
||||
|
||||
/* Versia Server API extension */
|
||||
export const SSOConfig = z.object({
|
||||
forced: z.boolean().openapi({
|
||||
forced: z.boolean().meta({
|
||||
description:
|
||||
"If this is enabled, normal identifier/password login is disabled and login must be done through SSO.",
|
||||
example: false,
|
||||
|
|
@ -103,21 +95,21 @@ export const SSOConfig = z.object({
|
|||
providers: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z.string().min(1).openapi({
|
||||
id: z.string().min(1).meta({
|
||||
description: "The ID of the provider.",
|
||||
example: "google",
|
||||
}),
|
||||
name: z.string().min(1).openapi({
|
||||
name: z.string().min(1).meta({
|
||||
description: "Human-readable provider name.",
|
||||
example: "Google",
|
||||
}),
|
||||
icon: z.string().url().optional().openapi({
|
||||
icon: z.url().optional().meta({
|
||||
description: "URL to the provider icon.",
|
||||
example: "https://cdn.versia.social/google-icon.png",
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.openapi({
|
||||
.meta({
|
||||
description:
|
||||
"An array of external OpenID Connect providers that users can link their accounts to.",
|
||||
}),
|
||||
|
|
@ -126,32 +118,32 @@ export const SSOConfig = z.object({
|
|||
/* Versia Server API extension */
|
||||
export const Challenge = z
|
||||
.object({
|
||||
id: Id.openapi({}).openapi({
|
||||
id: Id.meta({}).meta({
|
||||
description: "Challenge ID in the database.",
|
||||
example: "b4a7e0f0-8f6a-479b-910b-9265c070d5bd",
|
||||
}),
|
||||
algorithm: z.enum(["SHA-1", "SHA-256", "SHA-512"]).openapi({
|
||||
algorithm: z.enum(["SHA-1", "SHA-256", "SHA-512"]).meta({
|
||||
description: "Algorithm used to generate the challenge.",
|
||||
example: "SHA-1",
|
||||
}),
|
||||
challenge: z.string().openapi({
|
||||
challenge: z.string().meta({
|
||||
description: "Challenge to solve.",
|
||||
example: "1234567890",
|
||||
}),
|
||||
maxnumber: z.number().int().nonnegative().optional().openapi({
|
||||
maxnumber: z.number().int().nonnegative().optional().meta({
|
||||
description: "Maximum number to solve the challenge.",
|
||||
example: 100,
|
||||
}),
|
||||
salt: z.string().openapi({
|
||||
salt: z.string().meta({
|
||||
description: "Salt used to generate the challenge.",
|
||||
example: "1234567890",
|
||||
}),
|
||||
signature: z.string().openapi({
|
||||
signature: z.string().meta({
|
||||
description: "Signature of the challenge.",
|
||||
example: "1234567890",
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
.meta({
|
||||
description: "A cryptographic challenge to solve. Used for Captchas.",
|
||||
ref: "Challenge",
|
||||
id: "Challenge",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { OAuth2Client } from "@badgateway/oauth2-client";
|
||||
import type { z } from "zod";
|
||||
import type { z } from "zod/v4";
|
||||
import type { Account } from "../schemas/account.ts";
|
||||
import type { CredentialApplication } from "../schemas/application.ts";
|
||||
import type { Attachment } from "../schemas/attachment.ts";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue