diff --git a/api/api/v1/accounts/:id/block.ts b/api/api/v1/accounts/:id/block.ts index 31d655b6..9aded87c 100644 --- a/api/api/v1/accounts/:id/block.ts +++ b/api/api/v1/accounts/:id/block.ts @@ -6,10 +6,12 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Relationship as RelationshipSchema, +} from "@versia/client-ng/schemas"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", diff --git a/api/api/v1/accounts/:id/follow.ts b/api/api/v1/accounts/:id/follow.ts index 2c3e5424..87683bd4 100644 --- a/api/api/v1/accounts/:id/follow.ts +++ b/api/api/v1/accounts/:id/follow.ts @@ -6,11 +6,13 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Relationship as RelationshipSchema, + iso631, +} from "@versia/client-ng/schemas"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { iso631 } from "~/classes/schemas/common"; -import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; import { ErrorSchema } from "~/types/api"; const route = createRoute({ diff --git a/api/api/v1/accounts/:id/followers.ts b/api/api/v1/accounts/:id/followers.ts index 1d7c7cd9..52e2ce26 100644 --- a/api/api/v1/accounts/:id/followers.ts +++ b/api/api/v1/accounts/:id/followers.ts @@ -6,11 +6,10 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Account as AccountSchema } from "@versia/client-ng/schemas"; import { Timeline } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { Account } from "~/classes/schemas/account"; -import { Account as AccountSchema } from "~/classes/schemas/account"; const route = createRoute({ method: "get", @@ -63,7 +62,7 @@ const route = createRoute({ description: "Accounts which follow the given account.", content: { "application/json": { - schema: z.array(Account), + schema: z.array(AccountSchema), }, }, headers: z.object({ diff --git a/api/api/v1/accounts/:id/following.ts b/api/api/v1/accounts/:id/following.ts index afa75033..2339f184 100644 --- a/api/api/v1/accounts/:id/following.ts +++ b/api/api/v1/accounts/:id/following.ts @@ -6,11 +6,10 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Account as AccountSchema } from "@versia/client-ng/schemas"; import { Timeline } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { Account } from "~/classes/schemas/account"; -import { Account as AccountSchema } from "~/classes/schemas/account"; const route = createRoute({ method: "get", @@ -63,7 +62,7 @@ const route = createRoute({ description: "Accounts which the given account is following.", content: { "application/json": { - schema: z.array(Account), + schema: z.array(AccountSchema), }, }, headers: z.object({ diff --git a/api/api/v1/accounts/:id/index.ts b/api/api/v1/accounts/:id/index.ts index c770c756..3490d449 100644 --- a/api/api/v1/accounts/:id/index.ts +++ b/api/api/v1/accounts/:id/index.ts @@ -6,9 +6,8 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Account as AccountSchema } from "@versia/client-ng/schemas"; import { RolePermissions } from "@versia/kit/tables"; -import { Account } from "~/classes/schemas/account"; -import { Account as AccountSchema } from "~/classes/schemas/account"; const route = createRoute({ method: "get", @@ -37,7 +36,7 @@ const route = createRoute({ "The Account record will be returned. Note that acct of local users does not include the domain name.", content: { "application/json": { - schema: Account, + schema: AccountSchema, }, }, }, diff --git a/api/api/v1/accounts/:id/mute.ts b/api/api/v1/accounts/:id/mute.ts index c99688a1..7ec777b7 100644 --- a/api/api/v1/accounts/:id/mute.ts +++ b/api/api/v1/accounts/:id/mute.ts @@ -6,10 +6,12 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Relationship as RelationshipSchema, +} from "@versia/client-ng/schemas"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", diff --git a/api/api/v1/accounts/:id/note.ts b/api/api/v1/accounts/:id/note.ts index aef95afe..b4e4b554 100644 --- a/api/api/v1/accounts/:id/note.ts +++ b/api/api/v1/accounts/:id/note.ts @@ -6,10 +6,12 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Relationship as RelationshipSchema, +} from "@versia/client-ng/schemas"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", diff --git a/api/api/v1/accounts/:id/pin.ts b/api/api/v1/accounts/:id/pin.ts index 45b68149..f044c161 100644 --- a/api/api/v1/accounts/:id/pin.ts +++ b/api/api/v1/accounts/:id/pin.ts @@ -1,9 +1,11 @@ import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Relationship as RelationshipSchema, +} from "@versia/client-ng/schemas"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", diff --git a/api/api/v1/accounts/:id/refetch.ts b/api/api/v1/accounts/:id/refetch.ts index b84dab62..f5029124 100644 --- a/api/api/v1/accounts/:id/refetch.ts +++ b/api/api/v1/accounts/:id/refetch.ts @@ -6,10 +6,9 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Account as AccountSchema } from "@versia/client-ng/schemas"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; -import { Account } from "~/classes/schemas/account"; -import { Account as AccountSchema } from "~/classes/schemas/account"; import { ErrorSchema } from "~/types/api"; const route = createRoute({ @@ -36,7 +35,7 @@ const route = createRoute({ description: "Refetched account data", content: { "application/json": { - schema: Account, + schema: AccountSchema, }, }, }, diff --git a/api/api/v1/accounts/:id/remove_from_followers.ts b/api/api/v1/accounts/:id/remove_from_followers.ts index e6c3d4ba..8596d2e9 100644 --- a/api/api/v1/accounts/:id/remove_from_followers.ts +++ b/api/api/v1/accounts/:id/remove_from_followers.ts @@ -6,10 +6,12 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Relationship as RelationshipSchema, +} from "@versia/client-ng/schemas"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", diff --git a/api/api/v1/accounts/:id/roles/:role_id/index.ts b/api/api/v1/accounts/:id/roles/:role_id/index.ts index 4bb23ebf..c6d79024 100644 --- a/api/api/v1/accounts/:id/roles/:role_id/index.ts +++ b/api/api/v1/accounts/:id/roles/:role_id/index.ts @@ -1,10 +1,12 @@ import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Role as RoleSchema, +} from "@versia/client-ng/schemas"; import { Role } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Role as RoleSchema } from "~/classes/schemas/versia"; import { ErrorSchema } from "~/types/api"; const routePost = createRoute({ diff --git a/api/api/v1/accounts/:id/roles/index.ts b/api/api/v1/accounts/:id/roles/index.ts index 54de5505..4d6eb198 100644 --- a/api/api/v1/accounts/:id/roles/index.ts +++ b/api/api/v1/accounts/:id/roles/index.ts @@ -1,8 +1,10 @@ import { apiRoute, auth, withUserParam } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Role as RoleSchema, +} from "@versia/client-ng/schemas"; import { Role } from "@versia/kit/db"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Role as RoleSchema } from "~/classes/schemas/versia.ts"; const route = createRoute({ method: "get", diff --git a/api/api/v1/accounts/:id/statuses.ts b/api/api/v1/accounts/:id/statuses.ts index 8f7ff7e8..9502f710 100644 --- a/api/api/v1/accounts/:id/statuses.ts +++ b/api/api/v1/accounts/:id/statuses.ts @@ -6,12 +6,14 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Status as StatusSchema, + zBoolean, +} from "@versia/client-ng/schemas"; import { Timeline } from "@versia/kit/db"; import { Notes, RolePermissions } from "@versia/kit/tables"; import { and, eq, gt, gte, inArray, isNull, lt, or, sql } from "drizzle-orm"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { zBoolean } from "~/classes/schemas/common.ts"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const route = createRoute({ method: "get", diff --git a/api/api/v1/accounts/:id/unblock.ts b/api/api/v1/accounts/:id/unblock.ts index 2b18d341..e8e09e40 100644 --- a/api/api/v1/accounts/:id/unblock.ts +++ b/api/api/v1/accounts/:id/unblock.ts @@ -6,10 +6,12 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Relationship as RelationshipSchema, +} from "@versia/client-ng/schemas"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", diff --git a/api/api/v1/accounts/:id/unfollow.ts b/api/api/v1/accounts/:id/unfollow.ts index 6e39491d..78c56e0a 100644 --- a/api/api/v1/accounts/:id/unfollow.ts +++ b/api/api/v1/accounts/:id/unfollow.ts @@ -6,10 +6,12 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Relationship as RelationshipSchema, +} from "@versia/client-ng/schemas"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", diff --git a/api/api/v1/accounts/:id/unmute.ts b/api/api/v1/accounts/:id/unmute.ts index 9bab74fe..7f209c1b 100644 --- a/api/api/v1/accounts/:id/unmute.ts +++ b/api/api/v1/accounts/:id/unmute.ts @@ -6,10 +6,12 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Relationship as RelationshipSchema, +} from "@versia/client-ng/schemas"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", diff --git a/api/api/v1/accounts/:id/unpin.ts b/api/api/v1/accounts/:id/unpin.ts index 2dc32669..b894f97e 100644 --- a/api/api/v1/accounts/:id/unpin.ts +++ b/api/api/v1/accounts/:id/unpin.ts @@ -6,10 +6,12 @@ import { withUserParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Relationship as RelationshipSchema, +} from "@versia/client-ng/schemas"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", diff --git a/api/api/v1/accounts/familiar_followers/index.ts b/api/api/v1/accounts/familiar_followers/index.ts index f1c63534..6378410a 100644 --- a/api/api/v1/accounts/familiar_followers/index.ts +++ b/api/api/v1/accounts/familiar_followers/index.ts @@ -1,10 +1,12 @@ import { apiRoute, auth, qsQuery, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + FamiliarFollowers as FamiliarFollowersSchema, +} from "@versia/client-ng/schemas"; import { User, db } from "@versia/kit/db"; import { RolePermissions, type Users } from "@versia/kit/tables"; import { type InferSelectModel, sql } from "drizzle-orm"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { FamiliarFollowers as FamiliarFollowersSchema } from "~/classes/schemas/familiar-followers"; const route = createRoute({ method: "get", diff --git a/api/api/v1/accounts/id/index.ts b/api/api/v1/accounts/id/index.ts index 34a5bf4d..a973430d 100644 --- a/api/api/v1/accounts/id/index.ts +++ b/api/api/v1/accounts/id/index.ts @@ -1,11 +1,10 @@ import { accountNotFound, apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Account as AccountSchema } from "@versia/client-ng/schemas"; import { User } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, eq, isNull } from "drizzle-orm"; import { ApiError } from "~/classes/errors/api-error"; -import { Account } from "~/classes/schemas/account"; -import { Account as AccountSchema } from "~/classes/schemas/account"; const route = createRoute({ method: "get", @@ -31,7 +30,7 @@ const route = createRoute({ description: "Account", content: { "application/json": { - schema: Account, + schema: AccountSchema, }, }, }, diff --git a/api/api/v1/accounts/index.ts b/api/api/v1/accounts/index.ts index b504e091..b729ddbc 100644 --- a/api/api/v1/accounts/index.ts +++ b/api/api/v1/accounts/index.ts @@ -1,12 +1,12 @@ import { apiRoute, auth, jsonOrForm, reusedResponses } from "@/api"; import { tempmailDomains } from "@/tempmail"; import { createRoute, z } from "@hono/zod-openapi"; +import { zBoolean } from "@versia/client-ng/schemas"; import { User } from "@versia/kit/db"; import { Users } from "@versia/kit/tables"; import { and, eq, isNull } from "drizzle-orm"; import ISO6391 from "iso-639-1"; import { ApiError } from "~/classes/errors/api-error"; -import { zBoolean } from "~/classes/schemas/common"; import { config } from "~/config.ts"; const schema = z.object({ diff --git a/api/api/v1/accounts/lookup/index.ts b/api/api/v1/accounts/lookup/index.ts index 8ce23d1f..4a2b86df 100644 --- a/api/api/v1/accounts/lookup/index.ts +++ b/api/api/v1/accounts/lookup/index.ts @@ -6,12 +6,11 @@ import { reusedResponses, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Account as AccountSchema } from "@versia/client-ng/schemas"; import { Instance, User } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, eq, isNull } from "drizzle-orm"; import { ApiError } from "~/classes/errors/api-error"; -import { Account } from "~/classes/schemas/account"; -import { Account as AccountSchema } from "~/classes/schemas/account"; import { config } from "~/config.ts"; const route = createRoute({ @@ -39,7 +38,7 @@ const route = createRoute({ description: "Account", content: { "application/json": { - schema: Account, + schema: AccountSchema, }, }, }, diff --git a/api/api/v1/accounts/relationships/index.ts b/api/api/v1/accounts/relationships/index.ts index c5e20de8..ca9212d3 100644 --- a/api/api/v1/accounts/relationships/index.ts +++ b/api/api/v1/accounts/relationships/index.ts @@ -1,10 +1,12 @@ import { apiRoute, auth, qsQuery, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Relationship as RelationshipSchema, + zBoolean, +} from "@versia/client-ng/schemas"; import { Relationship } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { zBoolean } from "~/classes/schemas/common.ts"; -import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "get", diff --git a/api/api/v1/accounts/search/index.ts b/api/api/v1/accounts/search/index.ts index 6aa21726..e7e0d00f 100644 --- a/api/api/v1/accounts/search/index.ts +++ b/api/api/v1/accounts/search/index.ts @@ -1,12 +1,11 @@ import { apiRoute, auth, parseUserAddress } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Account as AccountSchema, zBoolean } from "@versia/client-ng/schemas"; import { User } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { eq, ilike, not, or, sql } from "drizzle-orm"; import stringComparison from "string-comparison"; import { ApiError } from "~/classes/errors/api-error"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { zBoolean } from "~/classes/schemas/common.ts"; export const route = createRoute({ method: "get", diff --git a/api/api/v1/accounts/update_credentials/index.ts b/api/api/v1/accounts/update_credentials/index.ts index 4075f680..ea1b882d 100644 --- a/api/api/v1/accounts/update_credentials/index.ts +++ b/api/api/v1/accounts/update_credentials/index.ts @@ -2,13 +2,12 @@ import { apiRoute, auth, jsonOrForm, reusedResponses } from "@/api"; import { mergeAndDeduplicate } from "@/lib"; import { sanitizedHtmlStrip } from "@/sanitization"; import { createRoute, z } from "@hono/zod-openapi"; +import { Account as AccountSchema, zBoolean } from "@versia/client-ng/schemas"; import { Emoji, User } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, eq, isNull } from "drizzle-orm"; import { ApiError } from "~/classes/errors/api-error"; import { contentToHtml } from "~/classes/functions/status"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { zBoolean } from "~/classes/schemas/common.ts"; import { config } from "~/config.ts"; const route = createRoute({ diff --git a/api/api/v1/accounts/verify_credentials/index.ts b/api/api/v1/accounts/verify_credentials/index.ts index 1ca7bdfa..507b1edd 100644 --- a/api/api/v1/accounts/verify_credentials/index.ts +++ b/api/api/v1/accounts/verify_credentials/index.ts @@ -1,6 +1,6 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { Account } from "~/classes/schemas/account"; +import { Account } from "@versia/client-ng/schemas"; const route = createRoute({ method: "get", diff --git a/api/api/v1/apps/index.ts b/api/api/v1/apps/index.ts index 78a3b946..9c5af7d7 100644 --- a/api/api/v1/apps/index.ts +++ b/api/api/v1/apps/index.ts @@ -1,11 +1,11 @@ import { apiRoute, jsonOrForm, reusedResponses } from "@/api"; import { randomString } from "@/math"; import { createRoute, z } from "@hono/zod-openapi"; -import { Application } from "@versia/kit/db"; import { Application as ApplicationSchema, CredentialApplication as CredentialApplicationSchema, -} from "~/classes/schemas/application"; +} from "@versia/client-ng/schemas"; +import { Application } from "@versia/kit/db"; const route = createRoute({ method: "post", diff --git a/api/api/v1/apps/verify_credentials/index.ts b/api/api/v1/apps/verify_credentials/index.ts index c1a8ef5c..e0f4eaa2 100644 --- a/api/api/v1/apps/verify_credentials/index.ts +++ b/api/api/v1/apps/verify_credentials/index.ts @@ -1,9 +1,9 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute } from "@hono/zod-openapi"; +import { Application as ApplicationSchema } from "@versia/client-ng/schemas"; import { Application } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; -import { Application as ApplicationSchema } from "~/classes/schemas/application"; const route = createRoute({ method: "get", diff --git a/api/api/v1/blocks/index.ts b/api/api/v1/blocks/index.ts index ac9698b0..26e09002 100644 --- a/api/api/v1/blocks/index.ts +++ b/api/api/v1/blocks/index.ts @@ -1,9 +1,9 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Account as AccountSchema } from "@versia/client-ng/schemas"; import { Timeline } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { Account as AccountSchema } from "~/classes/schemas/account"; const route = createRoute({ method: "get", diff --git a/api/api/v1/custom_emojis/index.ts b/api/api/v1/custom_emojis/index.ts index 5cb82028..d8ae11df 100644 --- a/api/api/v1/custom_emojis/index.ts +++ b/api/api/v1/custom_emojis/index.ts @@ -1,9 +1,9 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { CustomEmoji as CustomEmojiSchema } from "@versia/client-ng/schemas"; import { Emoji } from "@versia/kit/db"; import { Emojis, RolePermissions } from "@versia/kit/tables"; import { and, eq, isNull, or } from "drizzle-orm"; -import { CustomEmoji as CustomEmojiSchema } from "~/classes/schemas/emoji"; const route = createRoute({ method: "get", diff --git a/api/api/v1/emojis/:id/index.ts b/api/api/v1/emojis/:id/index.ts index 7445b228..f794b50b 100644 --- a/api/api/v1/emojis/:id/index.ts +++ b/api/api/v1/emojis/:id/index.ts @@ -7,9 +7,9 @@ import { } from "@/api"; import { mimeLookup } from "@/content_types"; import { createRoute, z } from "@hono/zod-openapi"; +import { CustomEmoji as CustomEmojiSchema } from "@versia/client-ng/schemas"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; -import { CustomEmoji as CustomEmojiSchema } from "~/classes/schemas/emoji"; import { config } from "~/config.ts"; import { ErrorSchema } from "~/types/api"; diff --git a/api/api/v1/emojis/index.ts b/api/api/v1/emojis/index.ts index 363317bb..d9fd2405 100644 --- a/api/api/v1/emojis/index.ts +++ b/api/api/v1/emojis/index.ts @@ -1,11 +1,11 @@ import { apiRoute, auth, jsonOrForm, reusedResponses } from "@/api"; import { mimeLookup } from "@/content_types"; import { createRoute, z } from "@hono/zod-openapi"; +import { CustomEmoji as CustomEmojiSchema } from "@versia/client-ng/schemas"; import { Emoji, Media } from "@versia/kit/db"; import { Emojis, RolePermissions } from "@versia/kit/tables"; import { and, eq, isNull, or } from "drizzle-orm"; import { ApiError } from "~/classes/errors/api-error"; -import { CustomEmoji as CustomEmojiSchema } from "~/classes/schemas/emoji"; import { config } from "~/config.ts"; const schema = z.object({ diff --git a/api/api/v1/favourites/index.ts b/api/api/v1/favourites/index.ts index fd2c871f..7de80ff7 100644 --- a/api/api/v1/favourites/index.ts +++ b/api/api/v1/favourites/index.ts @@ -1,9 +1,9 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Status as StatusSchema } from "@versia/client-ng/schemas"; import { Timeline } from "@versia/kit/db"; import { Notes, RolePermissions } from "@versia/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const route = createRoute({ method: "get", diff --git a/api/api/v1/follow_requests/:account_id/authorize.ts b/api/api/v1/follow_requests/:account_id/authorize.ts index 78bfbcb7..58606c6f 100644 --- a/api/api/v1/follow_requests/:account_id/authorize.ts +++ b/api/api/v1/follow_requests/:account_id/authorize.ts @@ -1,10 +1,12 @@ import { accountNotFound, apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Relationship as RelationshipSchema, +} from "@versia/client-ng/schemas"; import { Relationship, User } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", diff --git a/api/api/v1/follow_requests/:account_id/reject.ts b/api/api/v1/follow_requests/:account_id/reject.ts index b664dd4d..3bb27498 100644 --- a/api/api/v1/follow_requests/:account_id/reject.ts +++ b/api/api/v1/follow_requests/:account_id/reject.ts @@ -1,10 +1,12 @@ import { accountNotFound, apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Relationship as RelationshipSchema, +} from "@versia/client-ng/schemas"; import { Relationship, User } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; const route = createRoute({ method: "post", diff --git a/api/api/v1/follow_requests/index.ts b/api/api/v1/follow_requests/index.ts index 6e72eb73..616e446b 100644 --- a/api/api/v1/follow_requests/index.ts +++ b/api/api/v1/follow_requests/index.ts @@ -1,9 +1,9 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Account as AccountSchema } from "@versia/client-ng/schemas"; import { Timeline } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { Account as AccountSchema } from "~/classes/schemas/account"; const route = createRoute({ method: "get", diff --git a/api/api/v1/instance/extended_description.ts b/api/api/v1/instance/extended_description.ts index 905c96ba..11d4377c 100644 --- a/api/api/v1/instance/extended_description.ts +++ b/api/api/v1/instance/extended_description.ts @@ -1,7 +1,7 @@ import { apiRoute } from "@/api"; import { createRoute } from "@hono/zod-openapi"; +import { ExtendedDescription as ExtendedDescriptionSchema } from "@versia/client-ng/schemas"; import { markdownParse } from "~/classes/functions/status"; -import { ExtendedDescription as ExtendedDescriptionSchema } from "~/classes/schemas/extended-description"; import { config } from "~/config.ts"; const route = createRoute({ diff --git a/api/api/v1/instance/index.ts b/api/api/v1/instance/index.ts index e626cab3..18012dea 100644 --- a/api/api/v1/instance/index.ts +++ b/api/api/v1/instance/index.ts @@ -1,11 +1,11 @@ import { apiRoute, auth } from "@/api"; import { proxyUrl } from "@/response"; import { createRoute, type z } from "@hono/zod-openapi"; +import { InstanceV1 as InstanceV1Schema } from "@versia/client-ng/schemas"; import { Instance, Note, User } from "@versia/kit/db"; import { Users } from "@versia/kit/tables"; import { and, eq, isNull } from "drizzle-orm"; import { markdownParse } from "~/classes/functions/status"; -import { InstanceV1 as InstanceV1Schema } from "~/classes/schemas/instance-v1"; import { config } from "~/config.ts"; import manifest from "~/package.json"; diff --git a/api/api/v1/instance/privacy_policy.ts b/api/api/v1/instance/privacy_policy.ts index 6c3d9dd4..d59cf4fe 100644 --- a/api/api/v1/instance/privacy_policy.ts +++ b/api/api/v1/instance/privacy_policy.ts @@ -1,7 +1,7 @@ import { apiRoute, auth } from "@/api"; import { createRoute } from "@hono/zod-openapi"; +import { PrivacyPolicy as PrivacyPolicySchema } from "@versia/client-ng/schemas"; import { markdownParse } from "~/classes/functions/status"; -import { PrivacyPolicy as PrivacyPolicySchema } from "~/classes/schemas/privacy-policy"; import { config } from "~/config.ts"; const route = createRoute({ diff --git a/api/api/v1/instance/rules.ts b/api/api/v1/instance/rules.ts index 1b1a7eee..e4297af9 100644 --- a/api/api/v1/instance/rules.ts +++ b/api/api/v1/instance/rules.ts @@ -1,6 +1,6 @@ import { apiRoute, auth } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; -import { Rule as RuleSchema } from "~/classes/schemas/rule"; +import { Rule as RuleSchema } from "@versia/client-ng/schemas"; import { config } from "~/config.ts"; const route = createRoute({ diff --git a/api/api/v1/instance/terms_of_service.ts b/api/api/v1/instance/terms_of_service.ts index eb6e1587..bca5004b 100644 --- a/api/api/v1/instance/terms_of_service.ts +++ b/api/api/v1/instance/terms_of_service.ts @@ -1,7 +1,7 @@ import { apiRoute, auth } from "@/api"; import { createRoute } from "@hono/zod-openapi"; +import { TermsOfService as TermsOfServiceSchema } from "@versia/client-ng/schemas"; import { markdownParse } from "~/classes/functions/status"; -import { TermsOfService as TermsOfServiceSchema } from "~/classes/schemas/tos"; import { config } from "~/config.ts"; const route = createRoute({ diff --git a/api/api/v1/markers/index.ts b/api/api/v1/markers/index.ts index 61939897..1bd3c64f 100644 --- a/api/api/v1/markers/index.ts +++ b/api/api/v1/markers/index.ts @@ -1,11 +1,13 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Marker as MarkerSchema, + Notification as NotificationSchema, + Status as StatusSchema, +} from "@versia/client-ng/schemas"; import { db } from "@versia/kit/db"; import { Markers, RolePermissions } from "@versia/kit/tables"; import { type SQL, and, eq } from "drizzle-orm"; -import { Marker as MarkerSchema } from "~/classes/schemas/marker"; -import { Notification as NotificationSchema } from "~/classes/schemas/notification"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const MarkerResponseSchema = z.object({ notifications: MarkerSchema.optional(), diff --git a/api/api/v1/media/:id/index.ts b/api/api/v1/media/:id/index.ts index 6e0cf8d5..2ccddd40 100644 --- a/api/api/v1/media/:id/index.ts +++ b/api/api/v1/media/:id/index.ts @@ -1,9 +1,9 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Attachment as AttachmentSchema } from "@versia/client-ng/schemas"; import { Media } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; -import { Attachment as AttachmentSchema } from "~/classes/schemas/attachment"; import { ErrorSchema } from "~/types/api"; const routePut = createRoute({ diff --git a/api/api/v1/media/index.ts b/api/api/v1/media/index.ts index a8335b42..dbc8bbb7 100644 --- a/api/api/v1/media/index.ts +++ b/api/api/v1/media/index.ts @@ -1,8 +1,8 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Attachment as AttachmentSchema } from "@versia/client-ng/schemas"; import { Media } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { Attachment as AttachmentSchema } from "~/classes/schemas/attachment"; import { ErrorSchema } from "~/types/api"; const route = createRoute({ diff --git a/api/api/v1/mutes/index.ts b/api/api/v1/mutes/index.ts index 7d99c7b7..2355d63c 100644 --- a/api/api/v1/mutes/index.ts +++ b/api/api/v1/mutes/index.ts @@ -1,9 +1,9 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Account as AccountSchema } from "@versia/client-ng/schemas"; import { Timeline } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { Account as AccountSchema } from "~/classes/schemas/account"; const route = createRoute({ method: "get", diff --git a/api/api/v1/notifications/:id/dismiss.ts b/api/api/v1/notifications/:id/dismiss.ts index e9c4df6a..da14169c 100644 --- a/api/api/v1/notifications/:id/dismiss.ts +++ b/api/api/v1/notifications/:id/dismiss.ts @@ -1,9 +1,9 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Notification as NotificationSchema } from "@versia/client-ng/schemas"; import { Notification } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; -import { Notification as NotificationSchema } from "~/classes/schemas/notification"; const route = createRoute({ method: "post", diff --git a/api/api/v1/notifications/:id/index.ts b/api/api/v1/notifications/:id/index.ts index 9b52cef7..e6fcba6e 100644 --- a/api/api/v1/notifications/:id/index.ts +++ b/api/api/v1/notifications/:id/index.ts @@ -1,9 +1,9 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Notification as NotificationSchema } from "@versia/client-ng/schemas"; import { Notification } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; -import { Notification as NotificationSchema } from "~/classes/schemas/notification.ts"; import { ErrorSchema } from "~/types/api"; const route = createRoute({ diff --git a/api/api/v1/notifications/index.ts b/api/api/v1/notifications/index.ts index de4cc25c..abb19b03 100644 --- a/api/api/v1/notifications/index.ts +++ b/api/api/v1/notifications/index.ts @@ -1,11 +1,13 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Notification as NotificationSchema, + zBoolean, +} from "@versia/client-ng/schemas"; import { Timeline } from "@versia/kit/db"; import { Notifications, RolePermissions } from "@versia/kit/tables"; import { and, eq, gt, gte, inArray, lt, not, sql } from "drizzle-orm"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { zBoolean } from "~/classes/schemas/common.ts"; -import { Notification as NotificationSchema } from "~/classes/schemas/notification.ts"; const route = createRoute({ method: "get", diff --git a/api/api/v1/profile/avatar.ts b/api/api/v1/profile/avatar.ts index d5143d68..191269b6 100644 --- a/api/api/v1/profile/avatar.ts +++ b/api/api/v1/profile/avatar.ts @@ -1,7 +1,7 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute } from "@hono/zod-openapi"; +import { Account } from "@versia/client-ng/schemas"; import { RolePermissions } from "@versia/kit/tables"; -import { Account } from "~/classes/schemas/account"; const route = createRoute({ method: "delete", diff --git a/api/api/v1/profile/header.ts b/api/api/v1/profile/header.ts index 166f865c..a94faba7 100644 --- a/api/api/v1/profile/header.ts +++ b/api/api/v1/profile/header.ts @@ -1,7 +1,7 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute } from "@hono/zod-openapi"; +import { Account } from "@versia/client-ng/schemas"; import { RolePermissions } from "@versia/kit/tables"; -import { Account } from "~/classes/schemas/account"; const route = createRoute({ method: "delete", diff --git a/api/api/v1/push/subscription/index.get.ts b/api/api/v1/push/subscription/index.get.ts index 375e44d2..7d67e34b 100644 --- a/api/api/v1/push/subscription/index.get.ts +++ b/api/api/v1/push/subscription/index.get.ts @@ -1,8 +1,8 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute } from "@hono/zod-openapi"; +import { WebPushSubscription as WebPushSubscriptionSchema } from "@versia/client-ng/schemas"; import { PushSubscription } from "@versia/kit/db"; import { ApiError } from "~/classes/errors/api-error"; -import { WebPushSubscription as WebPushSubscriptionSchema } from "~/classes/schemas/pushsubscription"; import { RolePermissions } from "~/drizzle/schema"; export default apiRoute((app) => diff --git a/api/api/v1/push/subscription/index.post.ts b/api/api/v1/push/subscription/index.post.ts index 9a0e634c..ba3c54bf 100644 --- a/api/api/v1/push/subscription/index.post.ts +++ b/api/api/v1/push/subscription/index.post.ts @@ -1,9 +1,11 @@ import { apiRoute, reusedResponses } from "@/api"; import { auth, jsonOrForm } from "@/api"; import { createRoute } from "@hono/zod-openapi"; +import { + WebPushSubscriptionInput, + WebPushSubscription as WebPushSubscriptionSchema, +} from "@versia/client-ng/schemas"; import { PushSubscription } from "@versia/kit/db"; -import { WebPushSubscriptionInput } from "~/classes/schemas/pushsubscription"; -import { WebPushSubscription as WebPushSubscriptionSchema } from "~/classes/schemas/pushsubscription"; import { RolePermissions } from "~/drizzle/schema"; export default apiRoute((app) => diff --git a/api/api/v1/push/subscription/index.put.ts b/api/api/v1/push/subscription/index.put.ts index d08c1a0d..8a013b0b 100644 --- a/api/api/v1/push/subscription/index.put.ts +++ b/api/api/v1/push/subscription/index.put.ts @@ -1,11 +1,11 @@ import { apiRoute, auth, jsonOrForm, reusedResponses } from "@/api"; import { createRoute } from "@hono/zod-openapi"; -import { PushSubscription } from "@versia/kit/db"; -import { ApiError } from "~/classes/errors/api-error"; import { WebPushSubscriptionInput, WebPushSubscription as WebPushSubscriptionSchema, -} from "~/classes/schemas/pushsubscription"; +} from "@versia/client-ng/schemas"; +import { PushSubscription } from "@versia/kit/db"; +import { ApiError } from "~/classes/errors/api-error"; import { RolePermissions } from "~/drizzle/schema"; export default apiRoute((app) => diff --git a/api/api/v1/roles/:id/index.ts b/api/api/v1/roles/:id/index.ts index 9196f091..b5cc6187 100644 --- a/api/api/v1/roles/:id/index.ts +++ b/api/api/v1/roles/:id/index.ts @@ -1,9 +1,9 @@ import { apiRoute, auth } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Role as RoleSchema } from "@versia/client-ng/schemas"; import { Role } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; -import { Role as RoleSchema } from "~/classes/schemas/versia.ts"; import { ErrorSchema } from "~/types/api"; const routeGet = createRoute({ diff --git a/api/api/v1/roles/index.ts b/api/api/v1/roles/index.ts index adb6a839..21b72e66 100644 --- a/api/api/v1/roles/index.ts +++ b/api/api/v1/roles/index.ts @@ -1,8 +1,8 @@ import { apiRoute, auth } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Role as RoleSchema } from "@versia/client-ng/schemas"; import { Role } from "@versia/kit/db"; import { ApiError } from "~/classes/errors/api-error"; -import { Role as RoleSchema } from "~/classes/schemas/versia.ts"; import { RolePermissions } from "~/drizzle/schema"; import { ErrorSchema } from "~/types/api"; diff --git a/api/api/v1/statuses/:id/context.ts b/api/api/v1/statuses/:id/context.ts index a823f24e..53f5deac 100644 --- a/api/api/v1/statuses/:id/context.ts +++ b/api/api/v1/statuses/:id/context.ts @@ -6,9 +6,11 @@ import { withNoteParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Context as ContextSchema, + Status as StatusSchema, +} from "@versia/client-ng/schemas"; import { RolePermissions } from "@versia/kit/tables"; -import { Context as ContextSchema } from "~/classes/schemas/context"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const route = createRoute({ method: "get", diff --git a/api/api/v1/statuses/:id/favourite.ts b/api/api/v1/statuses/:id/favourite.ts index 88ed537e..4a41fdd6 100644 --- a/api/api/v1/statuses/:id/favourite.ts +++ b/api/api/v1/statuses/:id/favourite.ts @@ -6,8 +6,8 @@ import { withNoteParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Status as StatusSchema } from "@versia/client-ng/schemas"; import { RolePermissions } from "@versia/kit/tables"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const route = createRoute({ method: "post", diff --git a/api/api/v1/statuses/:id/favourited_by.ts b/api/api/v1/statuses/:id/favourited_by.ts index 74c607c2..216be8bb 100644 --- a/api/api/v1/statuses/:id/favourited_by.ts +++ b/api/api/v1/statuses/:id/favourited_by.ts @@ -6,11 +6,13 @@ import { withNoteParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Status as StatusSchema, +} from "@versia/client-ng/schemas"; import { Timeline } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const route = createRoute({ method: "get", diff --git a/api/api/v1/statuses/:id/index.ts b/api/api/v1/statuses/:id/index.ts index 5e66e6cc..acfa40a8 100644 --- a/api/api/v1/statuses/:id/index.ts +++ b/api/api/v1/statuses/:id/index.ts @@ -7,16 +7,16 @@ import { withNoteParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Attachment as AttachmentSchema, + PollOption, + Status as StatusSchema, + StatusSource as StatusSourceSchema, + zBoolean, +} from "@versia/client-ng/schemas"; import { Media } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; -import { Attachment as AttachmentSchema } from "~/classes/schemas/attachment"; -import { zBoolean } from "~/classes/schemas/common.ts"; -import { PollOption } from "~/classes/schemas/poll"; -import { - Status as StatusSchema, - StatusSource as StatusSourceSchema, -} from "~/classes/schemas/status"; import { config } from "~/config.ts"; const schema = z diff --git a/api/api/v1/statuses/:id/pin.ts b/api/api/v1/statuses/:id/pin.ts index 3795cbce..aed4011f 100644 --- a/api/api/v1/statuses/:id/pin.ts +++ b/api/api/v1/statuses/:id/pin.ts @@ -6,11 +6,11 @@ import { withNoteParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Status as StatusSchema } from "@versia/client-ng/schemas"; import { db } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import type { SQL } from "drizzle-orm"; import { ApiError } from "~/classes/errors/api-error"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const route = createRoute({ method: "post", diff --git a/api/api/v1/statuses/:id/reblog.ts b/api/api/v1/statuses/:id/reblog.ts index 6bc451e0..6e1dbdf6 100644 --- a/api/api/v1/statuses/:id/reblog.ts +++ b/api/api/v1/statuses/:id/reblog.ts @@ -7,10 +7,10 @@ import { withNoteParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Status as StatusSchema } from "@versia/client-ng/schemas"; import { Note } from "@versia/kit/db"; import { Notes, RolePermissions } from "@versia/kit/tables"; import { and, eq } from "drizzle-orm"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const route = createRoute({ method: "post", diff --git a/api/api/v1/statuses/:id/reblogged_by.ts b/api/api/v1/statuses/:id/reblogged_by.ts index ab7ca237..5477a3c7 100644 --- a/api/api/v1/statuses/:id/reblogged_by.ts +++ b/api/api/v1/statuses/:id/reblogged_by.ts @@ -6,11 +6,13 @@ import { withNoteParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Status as StatusSchema, +} from "@versia/client-ng/schemas"; import { Timeline } from "@versia/kit/db"; import { RolePermissions, Users } from "@versia/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const route = createRoute({ method: "get", diff --git a/api/api/v1/statuses/:id/source.ts b/api/api/v1/statuses/:id/source.ts index 4c101683..db2f644e 100644 --- a/api/api/v1/statuses/:id/source.ts +++ b/api/api/v1/statuses/:id/source.ts @@ -6,11 +6,11 @@ import { withNoteParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; -import { RolePermissions } from "@versia/kit/tables"; import { Status as StatusSchema, StatusSource as StatusSourceSchema, -} from "~/classes/schemas/status"; +} from "@versia/client-ng/schemas"; +import { RolePermissions } from "@versia/kit/tables"; const route = createRoute({ method: "get", diff --git a/api/api/v1/statuses/:id/unfavourite.ts b/api/api/v1/statuses/:id/unfavourite.ts index a4a848ba..1a0b117f 100644 --- a/api/api/v1/statuses/:id/unfavourite.ts +++ b/api/api/v1/statuses/:id/unfavourite.ts @@ -6,8 +6,8 @@ import { withNoteParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Status as StatusSchema } from "@versia/client-ng/schemas"; import { RolePermissions } from "@versia/kit/tables"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const route = createRoute({ method: "post", diff --git a/api/api/v1/statuses/:id/unpin.ts b/api/api/v1/statuses/:id/unpin.ts index 52b28854..ef2d00cf 100644 --- a/api/api/v1/statuses/:id/unpin.ts +++ b/api/api/v1/statuses/:id/unpin.ts @@ -6,9 +6,9 @@ import { withNoteParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Status as StatusSchema } from "@versia/client-ng/schemas"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const route = createRoute({ method: "post", diff --git a/api/api/v1/statuses/:id/unreblog.ts b/api/api/v1/statuses/:id/unreblog.ts index 19641e35..c601e883 100644 --- a/api/api/v1/statuses/:id/unreblog.ts +++ b/api/api/v1/statuses/:id/unreblog.ts @@ -6,11 +6,11 @@ import { withNoteParam, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Status as StatusSchema } from "@versia/client-ng/schemas"; import { Note } from "@versia/kit/db"; import { Notes, RolePermissions } from "@versia/kit/tables"; import { and, eq } from "drizzle-orm"; import { ApiError } from "~/classes/errors/api-error"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const route = createRoute({ method: "post", diff --git a/api/api/v1/statuses/index.ts b/api/api/v1/statuses/index.ts index 6780e975..fc9cb1ec 100644 --- a/api/api/v1/statuses/index.ts +++ b/api/api/v1/statuses/index.ts @@ -1,15 +1,15 @@ import { apiRoute, auth, jsonOrForm, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Attachment as AttachmentSchema, + PollOption, + Status as StatusSchema, + StatusSource as StatusSourceSchema, + zBoolean, +} from "@versia/client-ng/schemas"; import { Media, Note } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; import { ApiError } from "~/classes/errors/api-error"; -import { Attachment as AttachmentSchema } from "~/classes/schemas/attachment"; -import { zBoolean } from "~/classes/schemas/common.ts"; -import { PollOption } from "~/classes/schemas/poll"; -import { - Status as StatusSchema, - StatusSource as StatusSourceSchema, -} from "~/classes/schemas/status"; import { config } from "~/config.ts"; const schema = z diff --git a/api/api/v1/timelines/home.ts b/api/api/v1/timelines/home.ts index 4455bebd..fc76af14 100644 --- a/api/api/v1/timelines/home.ts +++ b/api/api/v1/timelines/home.ts @@ -1,9 +1,9 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Status as StatusSchema } from "@versia/client-ng/schemas"; import { Timeline } from "@versia/kit/db"; import { Notes, RolePermissions } from "@versia/kit/tables"; import { and, eq, gt, gte, inArray, lt, or, sql } from "drizzle-orm"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const route = createRoute({ method: "get", diff --git a/api/api/v1/timelines/public.ts b/api/api/v1/timelines/public.ts index aaebd997..d9e722a8 100644 --- a/api/api/v1/timelines/public.ts +++ b/api/api/v1/timelines/public.ts @@ -1,10 +1,9 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Status as StatusSchema, zBoolean } from "@versia/client-ng/schemas"; import { Timeline } from "@versia/kit/db"; import { Notes, RolePermissions } from "@versia/kit/tables"; import { and, eq, gt, gte, inArray, lt, or, sql } from "drizzle-orm"; -import { zBoolean } from "~/classes/schemas/common.ts"; -import { Status as StatusSchema } from "~/classes/schemas/status"; const route = createRoute({ method: "get", diff --git a/api/api/v2/filters/:id/index.ts b/api/api/v2/filters/:id/index.ts index 7ac2457b..6c2c47c3 100644 --- a/api/api/v2/filters/:id/index.ts +++ b/api/api/v2/filters/:id/index.ts @@ -1,14 +1,14 @@ import { apiRoute, auth, jsonOrForm, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + FilterKeyword as FilterKeywordSchema, + Filter as FilterSchema, + zBoolean, +} from "@versia/client-ng/schemas"; import { db } from "@versia/kit/db"; import { FilterKeywords, Filters, RolePermissions } from "@versia/kit/tables"; import { type SQL, and, eq, inArray } from "drizzle-orm"; import { ApiError } from "~/classes/errors/api-error"; -import { zBoolean } from "~/classes/schemas/common.ts"; -import { - FilterKeyword as FilterKeywordSchema, - Filter as FilterSchema, -} from "~/classes/schemas/filters"; import { ErrorSchema } from "~/types/api"; const routeGet = createRoute({ diff --git a/api/api/v2/filters/index.ts b/api/api/v2/filters/index.ts index 12a09b2a..c6fe9068 100644 --- a/api/api/v2/filters/index.ts +++ b/api/api/v2/filters/index.ts @@ -1,12 +1,12 @@ import { apiRoute, auth, jsonOrForm, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; -import { db } from "@versia/kit/db"; -import { FilterKeywords, Filters, RolePermissions } from "@versia/kit/tables"; -import type { SQL } from "drizzle-orm"; import { FilterKeyword as FilterKeywordSchema, Filter as FilterSchema, -} from "~/classes/schemas/filters"; +} from "@versia/client-ng/schemas"; +import { db } from "@versia/kit/db"; +import { FilterKeywords, Filters, RolePermissions } from "@versia/kit/tables"; +import type { SQL } from "drizzle-orm"; const routeGet = createRoute({ method: "get", diff --git a/api/api/v2/instance/index.ts b/api/api/v2/instance/index.ts index efb6e7e4..c9b0b4ee 100644 --- a/api/api/v2/instance/index.ts +++ b/api/api/v2/instance/index.ts @@ -1,10 +1,10 @@ import { apiRoute } from "@/api"; import { proxyUrl } from "@/response"; import { createRoute } from "@hono/zod-openapi"; +import { Instance as InstanceSchema } from "@versia/client-ng/schemas"; import { User } from "@versia/kit/db"; import { Users } from "@versia/kit/tables"; import { and, eq, isNull } from "drizzle-orm"; -import { Instance as InstanceSchema } from "~/classes/schemas/instance"; import { config } from "~/config.ts"; import pkg from "~/package.json"; diff --git a/api/api/v2/media/index.ts b/api/api/v2/media/index.ts index 5d6d9e7d..cf580235 100644 --- a/api/api/v2/media/index.ts +++ b/api/api/v2/media/index.ts @@ -1,8 +1,8 @@ import { apiRoute, auth, reusedResponses } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Attachment as AttachmentSchema } from "@versia/client-ng/schemas"; import { Media } from "@versia/kit/db"; import { RolePermissions } from "@versia/kit/tables"; -import { Attachment as AttachmentSchema } from "~/classes/schemas/attachment"; import { ErrorSchema } from "~/types/api"; const route = createRoute({ diff --git a/api/api/v2/search/index.ts b/api/api/v2/search/index.ts index 101aa5a0..b20f07d2 100644 --- a/api/api/v2/search/index.ts +++ b/api/api/v2/search/index.ts @@ -6,14 +6,16 @@ import { userAddressValidator, } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { + Account as AccountSchema, + Id, + Search as SearchSchema, + zBoolean, +} from "@versia/client-ng/schemas"; import { Note, User, db } from "@versia/kit/db"; import { Instances, Notes, RolePermissions, Users } from "@versia/kit/tables"; import { and, eq, inArray, isNull, sql } from "drizzle-orm"; import { ApiError } from "~/classes/errors/api-error"; -import { Account as AccountSchema } from "~/classes/schemas/account"; -import { Id } from "~/classes/schemas/common"; -import { zBoolean } from "~/classes/schemas/common.ts"; -import { Search as SearchSchema } from "~/classes/schemas/search"; import { searchManager } from "~/classes/search/search-manager"; import { config } from "~/config.ts"; import { ErrorSchema } from "~/types/api"; diff --git a/api/notes/:uuid/index.ts b/api/notes/:uuid/index.ts index edf30ce3..99de4146 100644 --- a/api/notes/:uuid/index.ts +++ b/api/notes/:uuid/index.ts @@ -1,11 +1,11 @@ import { apiRoute } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Status as StatusSchema } from "@versia/client-ng/schemas"; import { Note as NoteSchema } from "@versia/federation/schemas"; import { Note } from "@versia/kit/db"; import { Notes } from "@versia/kit/tables"; import { and, eq, inArray } from "drizzle-orm"; import { ApiError } from "~/classes/errors/api-error"; -import { Status as StatusSchema } from "~/classes/schemas/status"; import { config } from "~/config.ts"; import { ErrorSchema } from "~/types/api"; diff --git a/api/notes/:uuid/quotes.ts b/api/notes/:uuid/quotes.ts index 9a522722..935d3c23 100644 --- a/api/notes/:uuid/quotes.ts +++ b/api/notes/:uuid/quotes.ts @@ -1,12 +1,12 @@ import { apiRoute } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Status as StatusSchema } from "@versia/client-ng/schemas"; import { URICollection as URICollectionSchema } from "@versia/federation/schemas"; import type { URICollection } from "@versia/federation/types"; import { Note, db } from "@versia/kit/db"; import { Notes } from "@versia/kit/tables"; import { and, eq, inArray } from "drizzle-orm"; import { ApiError } from "~/classes/errors/api-error"; -import { Status as StatusSchema } from "~/classes/schemas/status"; import { config } from "~/config.ts"; import { ErrorSchema } from "~/types/api"; diff --git a/api/notes/:uuid/replies.ts b/api/notes/:uuid/replies.ts index 443669a9..67a4229d 100644 --- a/api/notes/:uuid/replies.ts +++ b/api/notes/:uuid/replies.ts @@ -1,12 +1,12 @@ import { apiRoute } from "@/api"; import { createRoute, z } from "@hono/zod-openapi"; +import { Status as StatusSchema } from "@versia/client-ng/schemas"; import { URICollection as URICollectionSchema } from "@versia/federation/schemas"; import type { URICollection } from "@versia/federation/types"; import { Note, db } from "@versia/kit/db"; import { Notes } from "@versia/kit/tables"; import { and, eq, inArray } from "drizzle-orm"; import { ApiError } from "~/classes/errors/api-error"; -import { Status as StatusSchema } from "~/classes/schemas/status"; import { config } from "~/config.ts"; import { ErrorSchema } from "~/types/api"; diff --git a/biome.json b/biome.json index 4ba5be90..e4641804 100644 --- a/biome.json +++ b/biome.json @@ -82,6 +82,18 @@ } } }, + "overrides": [ + { + "include": ["packages/client/versia/client.ts"], + "linter": { + "rules": { + "style": { + "useNamingConvention": "off" + } + } + } + } + ], "formatter": { "enabled": true, "indentStyle": "space", diff --git a/bun.lock b/bun.lock index 63a74a1b..e0851e1f 100644 --- a/bun.lock +++ b/bun.lock @@ -21,6 +21,7 @@ "@logtape/logtape": "^0.9.0", "@sentry/bun": "^9.5.0", "@versia/client": "^0.1.5", + "@versia/client-ng": "workspace:*", "@versia/federation": "^0.2.1", "@versia/kit": "workspace:*", "altcha-lib": "^1.2.0", @@ -85,6 +86,14 @@ "typescript": "^5.7.2", }, }, + "packages/client": { + "name": "@versia/client-ng", + "version": "0.2.0-alpha.1", + "dependencies": { + "@badgateway/oauth2-client": "^2.4.2", + "@hono/zod-openapi": "^0.19.2", + }, + }, "packages/plugin-kit": { "name": "@versia/kit", "version": "0.0.0", @@ -537,6 +546,8 @@ "@versia/client": ["@versia/client@0.1.5", "", { "dependencies": { "@badgateway/oauth2-client": "^2.4.2", "zod": "^3.24.1" } }, "sha512-POD2/IT98EZZ32kWEPc3XUY2zApX94tuBftNWIMyoT04Sp7CPuvv1TT2fxM2kmgrC6kgbh4I6yirPpzVY+FpSA=="], + "@versia/client-ng": ["@versia/client-ng@workspace:packages/client"], + "@versia/federation": ["@versia/federation@0.2.1", "", { "dependencies": { "magic-regexp": "^0.8.0", "mime-types": "^2.1.35", "zod": "^3.24.1", "zod-validation-error": "^3.4.0" } }, "sha512-FTo3VGNJBGmCi0ZEQMzqFZBbcfbX81kmg0UgY4cKamr1dJWgEf72IAZnEDgrBffFjYtreLGdEjFkkcq3JfS8oQ=="], "@versia/kit": ["@versia/kit@workspace:packages/plugin-kit"], diff --git a/classes/config/schema.ts b/classes/config/schema.ts index c47410c7..b93e7d12 100644 --- a/classes/config/schema.ts +++ b/classes/config/schema.ts @@ -5,17 +5,20 @@ import { RolePermissions, } from "@versia/kit/tables"; import { type BunFile, file } from "bun"; +import ISO6391 from "iso-639-1"; import { types as mimeTypes } from "mime-types"; import { generateVAPIDKeys } from "web-push"; import { ZodError } from "zod"; import { fromZodError } from "zod-validation-error"; -import { iso631 } from "../schemas/common.ts"; export enum MediaBackendType { Local = "local", S3 = "s3", } +// Need to declare this here instead of importing it otherwise we get cyclical import errors +export const iso631 = z.enum(ISO6391.getAllCodes() as [string, ...string[]]); + const urlPath = z .string() .trim() diff --git a/classes/database/application.ts b/classes/database/application.ts index 7713637d..22d5edf0 100644 --- a/classes/database/application.ts +++ b/classes/database/application.ts @@ -1,4 +1,8 @@ import type { z } from "@hono/zod-openapi"; +import type { + Application as ApplicationSchema, + CredentialApplication, +} from "@versia/client-ng/schemas"; import { Token, db } from "@versia/kit/db"; import { Applications } from "@versia/kit/tables"; import { @@ -9,10 +13,6 @@ import { eq, inArray, } from "drizzle-orm"; -import type { - Application as ApplicationSchema, - CredentialApplication, -} from "../schemas/application.ts"; import { BaseInterface } from "./base.ts"; type ApplicationType = InferSelectModel; diff --git a/classes/database/emoji.ts b/classes/database/emoji.ts index 41fe92d1..02f775d1 100644 --- a/classes/database/emoji.ts +++ b/classes/database/emoji.ts @@ -1,6 +1,7 @@ import { emojiValidatorWithColons, emojiValidatorWithIdentifiers } from "@/api"; import { proxyUrl } from "@/response"; import type { z } from "@hono/zod-openapi"; +import type { CustomEmoji } from "@versia/client-ng/schemas"; import type { CustomEmojiExtension } from "@versia/federation/types"; import { type Instance, Media, db } from "@versia/kit/db"; import { Emojis, type Instances, type Medias } from "@versia/kit/tables"; @@ -14,7 +15,6 @@ import { inArray, isNull, } from "drizzle-orm"; -import type { CustomEmoji } from "../schemas/emoji.ts"; import { BaseInterface } from "./base.ts"; type EmojiType = InferSelectModel & { diff --git a/classes/database/media.ts b/classes/database/media.ts index d36e91db..d2b5efbc 100644 --- a/classes/database/media.ts +++ b/classes/database/media.ts @@ -2,6 +2,7 @@ import { join } from "node:path"; import { mimeLookup } from "@/content_types.ts"; import { proxyUrl } from "@/response"; import type { z } from "@hono/zod-openapi"; +import type { Attachment as AttachmentSchema } from "@versia/client-ng/schemas"; import type { ContentFormat } from "@versia/federation/types"; import { db } from "@versia/kit/db"; import { Medias } from "@versia/kit/tables"; @@ -16,7 +17,6 @@ import { } from "drizzle-orm"; import sharp from "sharp"; import { MediaBackendType } from "~/classes/config/schema.ts"; -import type { Attachment as AttachmentSchema } from "~/classes/schemas/attachment.ts"; import { config } from "~/config.ts"; import { ApiError } from "../errors/api-error.ts"; import { getMediaHash } from "../media/media-hasher.ts"; diff --git a/classes/database/note.ts b/classes/database/note.ts index 5049035b..6fe4e565 100644 --- a/classes/database/note.ts +++ b/classes/database/note.ts @@ -4,6 +4,7 @@ import { sanitizedHtmlStrip } from "@/sanitization"; import { sentry } from "@/sentry"; import type { z } from "@hono/zod-openapi"; import { getLogger } from "@logtape/logtape"; +import type { Status, Status as StatusSchema } from "@versia/client-ng/schemas"; import { EntityValidator } from "@versia/federation"; import type { ContentFormat, @@ -36,10 +37,8 @@ import { findManyNotes, parseTextMentions, } from "~/classes/functions/status"; -import type { Status as StatusSchema } from "~/classes/schemas/status.ts"; import { config } from "~/config.ts"; import { DeliveryJobType, deliveryQueue } from "../queues/delivery.ts"; -import type { Status } from "../schemas/status.ts"; import { Application } from "./application.ts"; import { BaseInterface } from "./base.ts"; import { Emoji } from "./emoji.ts"; diff --git a/classes/database/notification.ts b/classes/database/notification.ts index 8a706ef3..8ba6e052 100644 --- a/classes/database/notification.ts +++ b/classes/database/notification.ts @@ -1,4 +1,5 @@ import type { z } from "@hono/zod-openapi"; +import type { Notification as NotificationSchema } from "@versia/client-ng/schemas"; import { Note, User, db } from "@versia/kit/db"; import { Notifications } from "@versia/kit/tables"; import { @@ -9,7 +10,6 @@ import { eq, inArray, } from "drizzle-orm"; -import type { Notification as NotificationSchema } from "~/classes/schemas/notification.ts"; import { transformOutputToUserWithRelations, userExtrasTemplate, diff --git a/classes/database/pushsubscription.ts b/classes/database/pushsubscription.ts index 218a1bc4..a1c98b26 100644 --- a/classes/database/pushsubscription.ts +++ b/classes/database/pushsubscription.ts @@ -1,4 +1,5 @@ import type { z } from "@hono/zod-openapi"; +import type { WebPushSubscription as WebPushSubscriptionSchema } from "@versia/client-ng/schemas"; import { type Token, type User, db } from "@versia/kit/db"; import { PushSubscriptions, Tokens } from "@versia/kit/tables"; import { @@ -9,7 +10,6 @@ import { eq, inArray, } from "drizzle-orm"; -import type { WebPushSubscription as WebPushSubscriptionSchema } from "../schemas/pushsubscription.ts"; import { BaseInterface } from "./base.ts"; type PushSubscriptionType = InferSelectModel; diff --git a/classes/database/relationship.ts b/classes/database/relationship.ts index 48827df1..58cac9a6 100644 --- a/classes/database/relationship.ts +++ b/classes/database/relationship.ts @@ -1,4 +1,5 @@ import { z } from "@hono/zod-openapi"; +import type { Relationship as RelationshipSchema } from "@versia/client-ng/schemas"; import { db } from "@versia/kit/db"; import { Relationships } from "@versia/kit/tables"; import { @@ -10,7 +11,6 @@ import { eq, inArray, } from "drizzle-orm"; -import type { Relationship as RelationshipSchema } from "~/classes/schemas/relationship"; import { BaseInterface } from "./base.ts"; import type { User } from "./user.ts"; diff --git a/classes/database/token.ts b/classes/database/token.ts index c1fdadab..2f50cb72 100644 --- a/classes/database/token.ts +++ b/classes/database/token.ts @@ -1,4 +1,5 @@ import type { z } from "@hono/zod-openapi"; +import type { Token as TokenSchema } from "@versia/client-ng/schemas"; import { type Application, User, db } from "@versia/kit/db"; import { Tokens } from "@versia/kit/tables"; import { @@ -9,7 +10,6 @@ import { eq, inArray, } from "drizzle-orm"; -import type { Token as TokenSchema } from "../schemas/token.ts"; import { BaseInterface } from "./base.ts"; type TokenType = InferSelectModel & { diff --git a/classes/database/user.ts b/classes/database/user.ts index a201f307..81f9af33 100644 --- a/classes/database/user.ts +++ b/classes/database/user.ts @@ -5,6 +5,11 @@ import { proxyUrl } from "@/response"; import { sentry } from "@/sentry"; import type { z } from "@hono/zod-openapi"; import { getLogger } from "@logtape/logtape"; +import type { + Account, + Mention as MentionSchema, + Source, +} from "@versia/client-ng/schemas"; import { EntityValidator, FederationRequester, @@ -51,8 +56,6 @@ import { config } from "~/config.ts"; import type { KnownEntity } from "~/types/api.ts"; import { DeliveryJobType, deliveryQueue } from "../queues/delivery.ts"; import { PushJobType, pushQueue } from "../queues/push.ts"; -import type { Account, Source } from "../schemas/account.ts"; -import type { Mention as MentionSchema } from "../schemas/status.ts"; import { BaseInterface } from "./base.ts"; import { Emoji } from "./emoji.ts"; import { Instance } from "./instance.ts"; diff --git a/drizzle/schema.ts b/drizzle/schema.ts index 14e548cf..31ca1822 100644 --- a/drizzle/schema.ts +++ b/drizzle/schema.ts @@ -1,4 +1,9 @@ import type { z } from "@hono/zod-openapi"; +import type { + Notification as NotificationSchema, + Source, + Status as StatusSchema, +} from "@versia/client-ng/schemas"; import type { ContentFormat, InstanceMetadata } from "@versia/federation/types"; import type { Challenge } from "altcha-lib/types"; import { relations, sql } from "drizzle-orm"; @@ -14,9 +19,6 @@ import { uniqueIndex, uuid, } from "drizzle-orm/pg-core"; -import type { Source } from "~/classes/schemas/account"; -import type { Notification as NotificationSchema } from "~/classes/schemas/notification.ts"; -import type { Status as StatusSchema } from "~/classes/schemas/status.ts"; // biome-ignore lint/nursery/useExplicitType: Type is too complex const createdAt = () => diff --git a/package.json b/package.json index 902f32b3..57292f7c 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "icon": "https://cdn.versia.pub/branding/icon.svg", "license": "AGPL-3.0-or-later", "keywords": ["federated", "activitypub", "bun"], - "workspaces": ["packages/plugin-kit"], + "workspaces": ["packages/*"], "maintainers": [ { "email": "contact@cpluspatch.com", @@ -95,6 +95,7 @@ "@versia/client": "^0.1.5", "@versia/federation": "^0.2.1", "@versia/kit": "workspace:*", + "@versia/client-ng": "workspace:*", "altcha-lib": "^1.2.0", "blurhash": "^2.0.5", "bullmq": "^5.43.1", diff --git a/packages/client/README.md b/packages/client/README.md new file mode 100644 index 00000000..29cff02b --- /dev/null +++ b/packages/client/README.md @@ -0,0 +1,141 @@ +

+ Versia Logo +

+ +

@versia/client

+ +TypeScript client API for Versia and Mastodon servers. + +## Efficiency + +The built output of the package is not even `32 KB` in size, making it a lightweight and efficient solution for your Versia needs. Installing the package adds around `5 MB` to your `node_modules` folder, but this does not affect the final bundle size. + +Compilation (bundling/minifying) time is a few seconds, almost all of which is spent on type-checking. The actual compilation time is less than a tenth of a second. + +## Usage + +This application may be used in the same was as [`megalodon`](https://github.com/h3poteto/megalodon). + +Initialize the client with the following code: + +```typescript +import { Client } from "@versia/client"; + +const baseUrl = new URL("https://versia.social"); +const accessToken = "..."; + +const client = new Client(baseUrl, accessToken); +``` + +The client can then be used to interact with the server: + +```typescript +const { data: status } = await client.postStatus("Hey there!"); +``` + +```typescript +const { data: posts } = await client.getHomeTimeline(); +``` + +Use your editor's IntelliSense to see all available methods and properties. JSDoc comments are always available. Method names are the same as with Megalodon, but with slight parameter changes in some cases. + +All methods have a special `extra` parameter that can be used to pass additional parameters to the underlying HTTP request. This can be used to pass query parameters, headers, etc.: + +```typescript +// extra is a RequestInit, the same as the second parameter of native fetch +const { data: posts } = await client.getHomeTimeline({ + headers: { "User-Agent": "MyApp/3" }, + signal: new AbortSignal(), +}); +``` + +## Getting Started + +### Prerequisites + +#### For Usage + +See the [**Compatibility**](#compatibility) section for the supported environments. Any package manager can be used to install the packages. + +#### For Development + +- [**Bun**](https://bun.sh) version `1.1.8` or higher. +- Either the [**Linux**](https://www.linux.org) or [**macOS**](https://www.apple.com/macos) operating systems. ([**Windows**](https://www.microsoft.com/windows) will work, but is not officially supported.) + +### Compatibility + +This library is built for JavaScript runtimes with the support for: + +- [**ES Modules**](https://nodejs.org/api/esm.html) +- [**ECMAScript 2020**](https://www.ecma-international.org/ecma-262/11.0/index.html) + +#### Runtimes + +- **Node.js**: 14.0+ is the minimum, but only Node.js 20.0+ (LTS) is officially supported. +- **Deno**: Support is unknown. 1.0+ is expected to work. +- **Bun**: Bun 1.1.8 is the minimum-supported version. As Bun is rapidly evolving, this may change. Previous versions may also work. + +#### Browsers + +Consequently, this library is compatible without any bundling in the following browser versions: + +- **Chrome**: 80+ +- **Edge**: 80+ +- **Firefox**: 74+ +- **Safari**: 13.1+ +- **Opera**: 67+ +- **Internet Explorer**: None + +If you are targeting older browsers, please don't, you are doing yourself a disservice. + +Transpilation to non-ES Module environments is not officially supported, but should be simple with the use of a bundler like [**Parcel**](https://parceljs.org) or [**Rollup**](https://rollupjs.org). + +### Installation + +Package is distributed as a scoped package on the NPM registry and [JSR](https://jsr.io). + +We strongly recommend using JSR over NPM for all your packages that are available on it. + +```bash +# NPM version +deno add npm:@versia/client # For Deno +npm install @versia/client # For NPM +yarn add @versia/client # For Yarn +pnpm add @versia/client # For PNPM +bun add @versia/client # For Bun + +# JSR version +deno add @versia/client # For Deno +npx jsr add @versia/client # For JSR +yarn dlx jsr add @versia/client # For Yarn +pnpm dlx jsr add @versia/client # For PNPM +bunx jsr add @versia/client # For Bun +``` + +#### From Source + +If you want to install from source, you can clone [this repository](https://github.com/versia-pub/api) and run the following commands: + +```bash +bun install # Install dependencies + +bun run build # Build the packages +``` + +The built package will be in the `client/dist` folder. + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Acknowledgments + +### Projects + +- [**Bun**](https://bun.sh): Thanks to the Bun team for creating an amazing JavaScript runtime. +- [**TypeScript**](https://www.typescriptlang.org): TypeScript is the backbone of this project. +- [**Node.js**](https://nodejs.org): Node.js created the idea of JavaScript on the server. + +### People + +- [**April John**](https://github.com/cutestnekoaqua): Creator and maintainer of the Versia Server ActivityPub bridge. diff --git a/packages/client/index.ts b/packages/client/index.ts new file mode 100644 index 00000000..3547eef2 --- /dev/null +++ b/packages/client/index.ts @@ -0,0 +1,3 @@ +// biome-ignore lint/performance/noBarrelFile: +export { type Output, ResponseError } from "./versia/base.ts"; +export { Client } from "./versia/client.ts"; diff --git a/packages/client/jsr.jsonc b/packages/client/jsr.jsonc new file mode 100644 index 00000000..c6b456b4 --- /dev/null +++ b/packages/client/jsr.jsonc @@ -0,0 +1,9 @@ +{ + "$schema": "https://jsr.io/schema/config-file.v1.json", + "name": "@versia/client", + "version": "0.2.0-alpha.1", + "exports": { + ".": "./index.ts", + "./schemas": "./schemas.ts" + } +} diff --git a/packages/client/package.json b/packages/client/package.json new file mode 100644 index 00000000..515c0d32 --- /dev/null +++ b/packages/client/package.json @@ -0,0 +1,61 @@ +{ + "name": "@versia/client-ng", + "displayName": "Versia Client", + "version": "0.2.0-alpha.1", + "author": { + "email": "jesse.wierzbinski@lysand.org", + "name": "Jesse Wierzbinski (CPlusPatch)", + "url": "https://cpluspatch.com" + }, + "readme": "README.md", + "repository": { + "type": "git", + "url": "https://github.com/versia-pub/server.git", + "directory": "packages/client" + }, + "bugs": { + "url": "https://github.com/versia-pub/server/issues" + }, + "license": "MIT", + "contributors": [ + { + "name": "Jesse Wierzbinski", + "email": "jesse.wierzbinski@lysand.org", + "url": "https://cpluspatch.com" + } + ], + "maintainers": [ + { + "name": "Jesse Wierzbinski", + "email": "jesse.wierzbinski@lysand.org", + "url": "https://cpluspatch.com" + } + ], + "description": "Client for Mastodon and Versia API", + "categories": ["Other"], + "type": "module", + "engines": { + "bun": ">=1.2.5" + }, + "exports": { + ".": { + "import": "./index.ts", + "default": "./index.ts" + }, + "./schemas": { + "import": "./schemas.ts", + "default": "./schemas.ts" + } + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/lysand" + }, + "homepage": "https://versia.pub", + "keywords": ["versia", "mastodon", "api", "typescript", "rest"], + "packageManager": "bun@1.2.5", + "dependencies": { + "@badgateway/oauth2-client": "^2.4.2", + "@hono/zod-openapi": "^0.19.2" + } +} diff --git a/packages/client/schemas.ts b/packages/client/schemas.ts new file mode 100644 index 00000000..7af54629 --- /dev/null +++ b/packages/client/schemas.ts @@ -0,0 +1,39 @@ +// biome-ignore lint/performance/noBarrelFile: +export { AccountWarning } from "./schemas/account-warning.ts"; +export { Account, Source, Field } from "./schemas/account.ts"; +export { Appeal } from "./schemas/appeal.ts"; +export { Application, CredentialApplication } from "./schemas/application.ts"; +export { Attachment } from "./schemas/attachment.ts"; +export { PreviewCard, PreviewCardAuthor } from "./schemas/card.ts"; +export { Context } from "./schemas/context.ts"; +export { CustomEmoji } from "./schemas/emoji.ts"; +export { ExtendedDescription } from "./schemas/extended-description.ts"; +export { FamiliarFollowers } from "./schemas/familiar-followers.ts"; +export { + Filter, + FilterKeyword, + FilterResult, + FilterStatus, +} from "./schemas/filters.ts"; +export { InstanceV1 } from "./schemas/instance-v1.ts"; +export { Instance } from "./schemas/instance.ts"; +export { Marker } from "./schemas/marker.ts"; +export { Notification } from "./schemas/notification.ts"; +export { Poll, PollOption } from "./schemas/poll.ts"; +export { Preferences } from "./schemas/preferences.ts"; +export { PrivacyPolicy } from "./schemas/privacy-policy.ts"; +export { + WebPushSubscription, + WebPushSubscriptionInput, +} from "./schemas/pushsubscription.ts"; +export { Relationship } from "./schemas/relationship.ts"; +export { Report } from "./schemas/report.ts"; +export { Rule } from "./schemas/rule.ts"; +export { Search } from "./schemas/search.ts"; +export { Status, Mention, StatusSource } from "./schemas/status.ts"; +export { Tag } from "./schemas/tag.ts"; +export { Token } from "./schemas/token.ts"; +export { TermsOfService } from "./schemas/tos.ts"; +export { Role, NoteReaction, SSOConfig } from "./schemas/versia.ts"; + +export { Id, iso631, zBoolean } from "./schemas/common.ts"; diff --git a/classes/schemas/account-warning.ts b/packages/client/schemas/account-warning.ts similarity index 100% rename from classes/schemas/account-warning.ts rename to packages/client/schemas/account-warning.ts diff --git a/classes/schemas/account.ts b/packages/client/schemas/account.ts similarity index 99% rename from classes/schemas/account.ts rename to packages/client/schemas/account.ts index 4ea23242..1fbaeb44 100644 --- a/classes/schemas/account.ts +++ b/packages/client/schemas/account.ts @@ -1,9 +1,8 @@ import { userAddressValidator } from "@/api.ts"; import { z } from "@hono/zod-openapi"; import type { Account as ApiAccount } from "@versia/client/types"; -import { zBoolean } from "~/classes/schemas/common.ts"; import { config } from "~/config.ts"; -import { iso631 } from "./common.ts"; +import { iso631, zBoolean } from "./common.ts"; import { CustomEmoji } from "./emoji.ts"; import { Role } from "./versia.ts"; diff --git a/classes/schemas/appeal.ts b/packages/client/schemas/appeal.ts similarity index 100% rename from classes/schemas/appeal.ts rename to packages/client/schemas/appeal.ts diff --git a/classes/schemas/application.ts b/packages/client/schemas/application.ts similarity index 100% rename from classes/schemas/application.ts rename to packages/client/schemas/application.ts diff --git a/classes/schemas/attachment.ts b/packages/client/schemas/attachment.ts similarity index 100% rename from classes/schemas/attachment.ts rename to packages/client/schemas/attachment.ts diff --git a/classes/schemas/card.ts b/packages/client/schemas/card.ts similarity index 100% rename from classes/schemas/card.ts rename to packages/client/schemas/card.ts diff --git a/classes/schemas/common.ts b/packages/client/schemas/common.ts similarity index 100% rename from classes/schemas/common.ts rename to packages/client/schemas/common.ts diff --git a/classes/schemas/context.ts b/packages/client/schemas/context.ts similarity index 100% rename from classes/schemas/context.ts rename to packages/client/schemas/context.ts diff --git a/classes/schemas/emoji.ts b/packages/client/schemas/emoji.ts similarity index 97% rename from classes/schemas/emoji.ts rename to packages/client/schemas/emoji.ts index 5b38aaa0..cc96a686 100644 --- a/classes/schemas/emoji.ts +++ b/packages/client/schemas/emoji.ts @@ -1,8 +1,7 @@ import { emojiValidator } from "@/api.ts"; import { z } from "@hono/zod-openapi"; -import { zBoolean } from "~/classes/schemas/common.ts"; import { config } from "~/config.ts"; -import { Id } from "./common.ts"; +import { Id, zBoolean } from "./common.ts"; export const CustomEmoji = z .object({ diff --git a/classes/schemas/extended-description.ts b/packages/client/schemas/extended-description.ts similarity index 100% rename from classes/schemas/extended-description.ts rename to packages/client/schemas/extended-description.ts diff --git a/classes/schemas/familiar-followers.ts b/packages/client/schemas/familiar-followers.ts similarity index 100% rename from classes/schemas/familiar-followers.ts rename to packages/client/schemas/familiar-followers.ts diff --git a/classes/schemas/filters.ts b/packages/client/schemas/filters.ts similarity index 100% rename from classes/schemas/filters.ts rename to packages/client/schemas/filters.ts diff --git a/classes/schemas/instance-v1.ts b/packages/client/schemas/instance-v1.ts similarity index 100% rename from classes/schemas/instance-v1.ts rename to packages/client/schemas/instance-v1.ts diff --git a/classes/schemas/instance.ts b/packages/client/schemas/instance.ts similarity index 100% rename from classes/schemas/instance.ts rename to packages/client/schemas/instance.ts diff --git a/classes/schemas/marker.ts b/packages/client/schemas/marker.ts similarity index 100% rename from classes/schemas/marker.ts rename to packages/client/schemas/marker.ts diff --git a/classes/schemas/notification.ts b/packages/client/schemas/notification.ts similarity index 100% rename from classes/schemas/notification.ts rename to packages/client/schemas/notification.ts diff --git a/classes/schemas/poll.ts b/packages/client/schemas/poll.ts similarity index 100% rename from classes/schemas/poll.ts rename to packages/client/schemas/poll.ts diff --git a/classes/schemas/preferences.ts b/packages/client/schemas/preferences.ts similarity index 100% rename from classes/schemas/preferences.ts rename to packages/client/schemas/preferences.ts diff --git a/classes/schemas/privacy-policy.ts b/packages/client/schemas/privacy-policy.ts similarity index 100% rename from classes/schemas/privacy-policy.ts rename to packages/client/schemas/privacy-policy.ts diff --git a/classes/schemas/pushsubscription.ts b/packages/client/schemas/pushsubscription.ts similarity index 100% rename from classes/schemas/pushsubscription.ts rename to packages/client/schemas/pushsubscription.ts diff --git a/classes/schemas/relationship.ts b/packages/client/schemas/relationship.ts similarity index 100% rename from classes/schemas/relationship.ts rename to packages/client/schemas/relationship.ts diff --git a/classes/schemas/report.ts b/packages/client/schemas/report.ts similarity index 100% rename from classes/schemas/report.ts rename to packages/client/schemas/report.ts diff --git a/classes/schemas/rule.ts b/packages/client/schemas/rule.ts similarity index 100% rename from classes/schemas/rule.ts rename to packages/client/schemas/rule.ts diff --git a/classes/schemas/search.ts b/packages/client/schemas/search.ts similarity index 100% rename from classes/schemas/search.ts rename to packages/client/schemas/search.ts diff --git a/classes/schemas/status.ts b/packages/client/schemas/status.ts similarity index 100% rename from classes/schemas/status.ts rename to packages/client/schemas/status.ts diff --git a/classes/schemas/tag.ts b/packages/client/schemas/tag.ts similarity index 100% rename from classes/schemas/tag.ts rename to packages/client/schemas/tag.ts diff --git a/classes/schemas/token.ts b/packages/client/schemas/token.ts similarity index 100% rename from classes/schemas/token.ts rename to packages/client/schemas/token.ts diff --git a/classes/schemas/tos.ts b/packages/client/schemas/tos.ts similarity index 100% rename from classes/schemas/tos.ts rename to packages/client/schemas/tos.ts diff --git a/classes/schemas/versia.ts b/packages/client/schemas/versia.ts similarity index 100% rename from classes/schemas/versia.ts rename to packages/client/schemas/versia.ts diff --git a/packages/client/versia/base.ts b/packages/client/versia/base.ts new file mode 100644 index 00000000..725d462b --- /dev/null +++ b/packages/client/versia/base.ts @@ -0,0 +1,306 @@ +import { DEFAULT_UA } from "./constants.ts"; + +type HttpVerb = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; +type ConvertibleObject = { + [key: string]: + | string + | number + | boolean + | File + | undefined + | null + | ConvertibleObject[] + | ConvertibleObject; +}; + +/** + * Output of a request. Contains the data and headers. + * @template ReturnType The type of the data returned by the request. + */ +export interface Output { + data: ReturnType; + ok: boolean; + raw: Response; +} + +const objectToFormData = ( + obj: ConvertibleObject, + formData = new FormData(), + parentKey = "", +): FormData => { + if (obj === undefined || obj === null) { + return formData; + } + + for (const key of Object.keys(obj)) { + const value = obj[key]; + const fullKey = parentKey ? `${parentKey}[${key}]` : key; + + if (value === undefined || value === null) { + continue; + } + + if (value instanceof File) { + formData.append(fullKey, value as Blob); + } else if (Array.isArray(value)) { + for (const [index, item] of value.entries()) { + const arrayKey = `${fullKey}[${index}]`; + if (item instanceof File) { + formData.append(arrayKey, item as Blob); + } else if (typeof item === "object") { + objectToFormData( + item as ConvertibleObject, + formData, + arrayKey, + ); + } else { + formData.append(arrayKey, String(item)); + } + } + } else if (typeof value === "object") { + objectToFormData(value as ConvertibleObject, formData, fullKey); + } else { + formData.append(fullKey, String(value)); + } + } + + return formData; +}; + +/** + * Wrapper around Error, useful for detecting if an error + * is due to a failed request. + * + * Throws if the request returns invalid or unexpected data. + */ +export class ResponseError< + ReturnType = { + error?: string; + }, +> extends Error { + public constructor( + public response: Output, + message: string, + ) { + super(message); + this.name = "ResponseError"; + } +} + +export class BaseClient { + public constructor( + protected baseUrl: URL, + private accessToken?: string, + public globalCatch: (error: ResponseError) => void = () => { + // Do nothing by default + }, + ) {} + + public get url(): URL { + return this.baseUrl; + } + + public get token(): string | undefined { + return this.accessToken; + } + + private async request( + request: Request, + ): Promise> { + const result = await fetch(request); + const isJson = result.headers + .get("Content-Type") + ?.includes("application/json"); + + if (!result.ok) { + const error = isJson ? await result.json() : await result.text(); + throw new ResponseError( + { + data: error, + ok: false, + raw: result, + }, + `Request failed (${result.status}): ${ + error.error || error.message || result.statusText + }`, + ); + } + + return { + data: isJson ? await result.json() : (await result.text()) || null, + ok: true, + raw: result, + }; + } + + private constructRequest( + path: string, + method: HttpVerb, + body?: object | FormData, + extra?: RequestInit, + ): Request { + const headers = new Headers({ + "User-Agent": DEFAULT_UA, + }); + + if (this.accessToken) { + headers.set("Authorization", `Bearer ${this.accessToken}`); + } + if (body) { + if (!(body instanceof FormData)) { + headers.set("Content-Type", "application/json; charset=utf-8"); + } // else: let FormData set the content type, as it knows best (boundary, etc.) + } + + for (const [key, value] of Object.entries(extra?.headers || {})) { + headers.set(key, value); + } + + return new Request(new URL(path, this.baseUrl).toString(), { + method, + headers, + body: body + ? body instanceof FormData + ? body + : JSON.stringify(body) + : undefined, + ...extra, + }); + } + + public get( + path: string, + extra?: RequestInit, + ): Promise> { + return this.request( + this.constructRequest(path, "GET", undefined, extra), + ).catch((e) => { + this.globalCatch(e); + throw e; + }); + } + + public post( + path: string, + body?: object, + extra?: RequestInit, + ): Promise> { + return this.request( + this.constructRequest(path, "POST", body, extra), + ).catch((e) => { + this.globalCatch(e); + throw e; + }); + } + + public postForm( + path: string, + body: FormData | ConvertibleObject, + extra?: RequestInit, + ): Promise> { + return this.request( + this.constructRequest( + path, + "POST", + body instanceof FormData ? body : objectToFormData(body), + extra, + ), + ).catch((e) => { + this.globalCatch(e); + throw e; + }); + } + + public put( + path: string, + body?: object, + extra?: RequestInit, + ): Promise> { + return this.request( + this.constructRequest(path, "PUT", body, extra), + ).catch((e) => { + this.globalCatch(e); + throw e; + }); + } + + public putForm( + path: string, + body: FormData | ConvertibleObject, + extra?: RequestInit, + ): Promise> { + return this.request( + this.constructRequest( + path, + "PUT", + body instanceof FormData ? body : objectToFormData(body), + extra, + ), + ).catch((e) => { + this.globalCatch(e); + throw e; + }); + } + + public patch( + path: string, + body?: object, + extra?: RequestInit, + ): Promise> { + return this.request( + this.constructRequest(path, "PATCH", body, extra), + ).catch((e) => { + this.globalCatch(e); + throw e; + }); + } + + public patchForm( + path: string, + body: FormData | ConvertibleObject, + extra?: RequestInit, + ): Promise> { + return this.request( + this.constructRequest( + path, + "PATCH", + body instanceof FormData ? body : objectToFormData(body), + extra, + ), + ).catch((e) => { + this.globalCatch(e); + throw e; + }); + } + + public delete( + path: string, + body?: object, + extra?: RequestInit, + ): Promise> { + return this.request( + this.constructRequest(path, "DELETE", body, extra), + ).catch((e) => { + this.globalCatch(e); + throw e; + }); + } + + public deleteForm( + path: string, + body: FormData | ConvertibleObject, + extra?: RequestInit, + ): Promise> { + return this.request( + this.constructRequest( + path, + "DELETE", + body instanceof FormData ? body : objectToFormData(body), + extra, + ), + ).catch((e) => { + this.globalCatch(e); + throw e; + }); + } +} diff --git a/packages/client/versia/client.ts b/packages/client/versia/client.ts new file mode 100644 index 00000000..6dc3d1e8 --- /dev/null +++ b/packages/client/versia/client.ts @@ -0,0 +1,3103 @@ +// biome-ignore lint/correctness/noUndeclaredDependencies: biome is looking at the wrong package.json +import { OAuth2Client } from "@badgateway/oauth2-client"; +import type { z } from "zod"; +import type { Account } from "../schemas/account.ts"; +import type { CredentialApplication } from "../schemas/application.ts"; +import type { Attachment } from "../schemas/attachment.ts"; +import type { Context } from "../schemas/context.ts"; +import type { CustomEmoji } from "../schemas/emoji.ts"; +import type { ExtendedDescription } from "../schemas/extended-description.ts"; +import type { Instance } from "../schemas/instance.ts"; +import type { Marker } from "../schemas/marker.ts"; +import type { Notification } from "../schemas/notification.ts"; +import type { Poll } from "../schemas/poll.ts"; +import type { Preferences } from "../schemas/preferences.ts"; +import type { PrivacyPolicy } from "../schemas/privacy-policy.ts"; +import type { WebPushSubscription } from "../schemas/pushsubscription.ts"; +import type { Relationship } from "../schemas/relationship.ts"; +import type { Report } from "../schemas/report.ts"; +import type { Search } from "../schemas/search.ts"; +import type { Status, StatusSource } from "../schemas/status.ts"; +import type { Tag } from "../schemas/tag.ts"; +import type { Token } from "../schemas/token.ts"; +import type { TermsOfService } from "../schemas/tos.ts"; +import type { Role } from "../schemas/versia.ts"; +import { BaseClient, type Output } from "./base.ts"; +import { DEFAULT_SCOPE, NO_REDIRECT } from "./constants.ts"; + +type StatusContentType = + | "text/plain" + | "text/markdown" + | "text/html" + | "text/x.misskeymarkdown"; + +/** + * Client is a client for interacting with the Versia API. + * + * @extends BaseClient + * @example + * const client = new Client(new URL("https://example.com")); + * + * const { data } = await client.getInstance(); + * + * console.log(data); + */ +export class Client extends BaseClient { + /** + * POST /api/v1/follow_requests/:id/authorize + * + * @param id Target account ID. + * @return Relationship. + */ + public acceptFollowRequest( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/follow_requests/${id}/authorize`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/lists/:id/accounts + * + * @param id Target list ID. + * @param account_ids Array of account IDs to add to the list. + */ + public addAccountToList( + id: string, + account_ids: string[], + extra?: RequestInit, + ): Promise> { + return this.post( + `/api/v1/lists/${id}/accounts`, + { account_ids }, + extra, + ); + } + + /** + * PUT /api/v1/announcements/:id/reactions/:name + * + * @param id The ID of the Announcement in the database. + * @param name Unicode emoji, or the shortcode of a custom emoji. + */ + public addReactionToAnnouncement( + id: string, + name: string, + extra?: RequestInit, + ): Promise> { + return this.put( + `/api/v1/announcements/${id}/reactions/${name}`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/accounts/:account_id/roles/:role_id + * + * Versia API only. + * @param account_id The account ID. + * @param role_id The role ID. + */ + public assignRole( + account_id: string, + role_id: string, + extra?: RequestInit, + ): Promise> { + return this.post( + `/api/v1/accounts/${account_id}/roles/${role_id}`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/accounts/:id/block + * + * @param id The account ID. + * @return Relationship + */ + public blockAccount( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/accounts/${id}/block`, + undefined, + extra, + ); + } + + /** + * POST/api/v1/domain_blocks + * + * @param domain Domain to block. + */ + public blockDomain( + domain: string, + extra?: RequestInit, + ): Promise> { + return this.post("/api/v1/domain_blocks", { domain }, extra); + } + + public bookmarkStatus( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/statuses/${id}/bookmark`, + undefined, + extra, + ); + } + + /** + * DELETE /api/v1/scheduled_statuses/:id + * + * @param id Target scheduled status ID. + */ + public cancelScheduledStatus( + id: string, + extra?: RequestInit, + ): Promise> { + return this.delete( + `/api/v1/scheduled_statuses/${id}/cancel`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/apps + * + * Create an application. + * @param client_name your application's name + * @param options.redirect_uris Redirect URI for the application. Defaults to `urn:ietf:wg:oauth:2.0:oob`. + * @param options.scopes Array of scopes to request. + * @param options.website URL to the application's website. + */ + public createApp( + client_name: string, + options?: Partial<{ + redirect_uris: string; + scopes: string[]; + website?: string; + }>, + ): Promise>> { + return this.postForm>( + "/api/v1/apps", + { + client_name, + ...options, + scopes: options?.scopes?.join(" ") || DEFAULT_SCOPE.join(" "), + redirect_uris: options?.redirect_uris || NO_REDIRECT, + }, + ); + } + + /** + * POST /api/v1/statuses/:id/reactions/:emoji + * + * @param id The target status ID. + * @param emoji The emoji to add (shortcode or Unicode emoji). + * @returns + */ + public createEmojiReaction( + id: string, + emoji: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/statuses/${id}/reactions/${emoji}`, + undefined, + extra, + ); + } + + // FIXME: No FeaturedTag schema + /** + * POST /api/v1/featured_tags + * + * @param name Target hashtag name. + * @return FeaturedTag. + */ + public createFeaturedTag( + name: string, + extra?: RequestInit, + ): Promise> { + return this.post("/api/v1/featured_tags", { name }, extra); + } + + // FIXME: No List schema + /** + * POST /api/v1/lists + * + * @param title List name. + * @return List. + */ + public createList( + title: string, + extra?: RequestInit, + ): Promise> { + return this.post("/api/v1/lists", { title }, extra); + } + + /** + * POST /api/v1/roles + * + * Versia API only. + * @param name Name of the role. + * @param options.permissions Array of permissions. + * @param options.priority Role priority. + * @param options.description Role description. + * @param options.visible Role visibility. + * @param options.icon Role icon (URL). + * @returns Role + */ + public createRole( + name: string, + options: Partial<{ + permissions: string[]; + priority: number; + description: string; + visible: boolean; + icon: string; + }>, + extra?: RequestInit, + ): Promise>> { + return this.post>( + "/api/v1/roles", + { name, ...options }, + extra, + ); + } + + /** + * DELETE /api/v1/lists/:id/accounts + * + * @param id Target list ID. + * @param account_ids Array of account IDs to add to the list. + */ + public deleteAccountsFromList( + id: string, + account_ids: string[], + extra?: RequestInit, + ): Promise> { + return this.delete( + `/api/v1/lists/${id}/accounts`, + { account_ids }, + extra, + ); + } + + /** + * DELETE /api/v1/conversations/:id + * + * @param id Target conversation ID. + */ + public deleteConversation( + id: string, + extra?: RequestInit, + ): Promise> { + return this.delete( + `/api/v1/conversations/${id}`, + undefined, + extra, + ); + } + + /** + * DELETE /api/v1/emojis/:id + * + * Versia API only. + * @param id The emoji to delete's ID. + * @return Empty. + */ + public deleteEmoji(id: string, extra?: RequestInit): Promise> { + return this.delete(`/api/v1/emojis/${id}`, undefined, extra); + } + + /** + * DELETE /api/v1/statuses/:id/reactions/:emoji + * + * @param id The target status ID. + * @param emoji The emoji to delete (shortcode or Unicode emoji). + * @returns + */ + public deleteEmojiReaction( + id: string, + emoji: string, + extra?: RequestInit, + ): Promise>> { + return this.delete>( + `/api/v1/statuses/${id}/reactions/${emoji}`, + undefined, + extra, + ); + } + + /** + * DELETE /api/v1/featured_tags/:id + * + * @param id Target featured tag ID. + * @return Empty + */ + public deleteFeaturedTag( + id: string, + extra?: RequestInit, + ): Promise> { + return this.delete( + `/api/v1/featured_tags/${id}`, + undefined, + extra, + ); + } + + /** + * DELETE /api/v1/lists/:id + * + * @param id Target list ID. + */ + public deleteList(id: string, extra?: RequestInit): Promise> { + return this.delete(`/api/v1/lists/${id}`, undefined, extra); + } + + /** + * DELETE /api/v1/push/subscription + */ + public deletePushSubscription(extra?: RequestInit): Promise> { + return this.delete("/api/v1/push/subscription", undefined, extra); + } + + /** + * DELETE /api/v1/roles/:id + * + * Versia API only. + * @param id The role ID. + * @return Empty. + */ + public deleteRole(id: string, extra?: RequestInit): Promise> { + return this.delete(`/api/v1/roles/${id}`, undefined, extra); + } + + /** + * DELETE /api/v1/statuses/:id + * + * @param id The target status id. + * @return Status + */ + public deleteStatus( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.delete>( + `/api/v1/statuses/${id}`, + undefined, + extra, + ); + } + + // TODO: directStreaming + + /** + * POST /api/v1/announcements/:id/dismiss + * + * @param id The ID of the Announcement in the database. + */ + public dismissInstanceAnnouncement( + id: string, + extra?: RequestInit, + ): Promise> { + return this.post( + `/api/v1/instance/announcements/${id}/dismiss`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/notifications/:id/dismiss + * + * @param id Target notification ID. + */ + public dismissNotification( + id: string, + extra?: RequestInit, + ): Promise> { + return this.post( + `/api/v1/notifications/${id}/dismiss`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/notifications/clear + */ + public dismissNotifications(extra?: RequestInit): Promise> { + return this.post("/api/v1/notifications/clear", undefined, extra); + } + + /** + * PUT /api/v1/statuses/:id + * + * @param id The target status id. + * @param options.status The new status text. + * @param options.content_type The new status content type. + * @param options.media_ids Array of media IDs to attach to the status. + * @param options.poll Poll options. + * @param options.sensitive Mark status as sensitive. + * @param options.spoiler_text Warning text that should be displayed as a warning before the actual content. + * @param options.language Language of the status (ISO 639-1 two letter code). + * @return Status + */ + public editStatus( + id: string, + options: Partial<{ + status: string; + content_type: StatusContentType; + media_ids: string[]; + poll: Partial<{ + expires_in: number; + hide_totals: boolean; + multiple: boolean; + options: string[]; + }>; + sensitive: boolean; + spoiler_text: string; + language: string; + }>, + extra?: RequestInit, + ): Promise>> { + return this.put>( + `/api/v1/statuses/${id}`, + options, + extra, + ); + } + + /** + * POST /api/v1/statuses/:id/favourite + * + * @param id The target status id. + * @return Status. + */ + public favouriteStatus( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/statuses/${id}/favourite`, + undefined, + extra, + ); + } + + /** + * POST /oauth/token + * + * Fetch OAuth access token. + * Get an access token based client_id and client_secret and authorization code. + * @param client_id Will be generated by #createApp or #registerApp + * @param client_secret Will be generated by #createApp or #registerApp + * @param code Will be generated by the link of #generateAuthUrl or #registerApp + * @param redirect_uri Must be the same URI as the time when you register your OAuth2 application + */ + public fetchAccessToken( + client_id: string, + client_secret: string, + code?: string, + redirect_uri: string = NO_REDIRECT, + extra?: RequestInit, + ): Promise>> { + return this.postForm>( + "/oauth/token", + { + client_id, + client_secret, + code, + redirect_uri, + grant_type: "authorization_code", + }, + extra, + ); + } + + /** + * POST /api/v1/accounts/:id/follow + * + * @param id The account ID. + * @param options.reblogs Receive this account's reblogs in home timeline. + * @param options.notify Receive push notifications from this account. + * @param options.languages Array of language codes. + * @return Relationship + */ + public followAccount( + id: string, + options?: Partial<{ + reblogs: boolean; + notify: boolean; + languages: string[]; + }>, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/accounts/${id}/follow`, + options, + extra, + ); + } + + /** + * POST /api/v1/tags/:id/follow + * + * @param id Target hashtag id. + * @return Tag + */ + public followTag( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/tags/${id}/follow`, + undefined, + extra, + ); + } + + /** + * Generate an authorization URL for the client. + * + * @param client_id OAuth2 app client ID. + * @param client_secret OAuth2 app client secret. + * @param options.redirect_uri Redirect URI for the OAuth2 app. + * @param options.scopes Array of scopes to request. + * @returns + */ + public generateAuthUrl( + client_id: string, + client_secret: string, + options: Partial<{ + redirect_uri: string; + scopes: string[]; + }>, + ): Promise { + const oauthClient = new OAuth2Client({ + server: this.baseUrl.toString(), + clientId: client_id, + clientSecret: client_secret, + tokenEndpoint: "/oauth/token", + authorizationEndpoint: "/oauth/authorize", + }); + + return oauthClient.authorizationCode.getAuthorizeUri({ + redirectUri: options.redirect_uri || NO_REDIRECT, + scope: options.scopes || DEFAULT_SCOPE, + }); + } + + /** + * GET /api/v1/accounts/:id + * + * @param id The account ID. + * @return An account. + */ + public getAccount( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.get>( + `/api/v1/accounts/${id}`, + extra, + ); + } + + /** + * GET /api/v1/accounts/id + * + * Versia API only. + * @param username The username. + * @return An account. + */ + public getAccountByUsername( + username: string, + extra?: RequestInit, + ): Promise>> { + return this.get>( + `/api/v1/accounts/id?${new URLSearchParams({ + username, + }).toString()}`, + extra, + ); + } + + /** + * GET /api/v1/accounts/:id/followers + * + * @param id The account ID. + * @param options.limit Max number of results to return. Defaults to 40. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @return The array of accounts. + */ + public getAccountFollowers( + id: string, + options?: Partial<{ + max_id: string; + since_id: string; + limit: number; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>( + `/api/v1/accounts/${id}/followers?${params}`, + extra, + ); + } + + /** + * GET /api/v1/accounts/:id/following + * + * @param id The account ID. + * @param options.limit Max number of results to return. Defaults to 40. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @return The array of accounts. + */ + public getAccountFollowing( + id: string, + options?: Partial<{ + max_id: string; + since_id: string; + limit: number; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>( + `/api/v1/accounts/${id}/following?${params}`, + extra, + ); + } + + // FIXME: No List schema + /** + * GET /api/v1/accounts/:id/lists + * + * @param id The account ID. + * @return The array of lists. + */ + public getAccountLists( + id: string, + extra?: RequestInit, + ): Promise> { + return this.get(`/api/v1/accounts/${id}/lists`, extra); + } + + /** + * GET /api/v1/accounts/:id/roles + * + * Versia API only. + * @param id The account ID. + * @return Array of roles. + */ + public getAccountRoles( + id: string, + extra?: RequestInit, + ): Promise[]>> { + return this.get[]>( + `/api/v1/accounts/${id}/roles`, + extra, + ); + } + + /** + * GET /api/v1/accounts/:id/statuses + * + * @param id The account ID. + * @param options.limit Max number of results to return. Defaults to 20. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID but starting with most recent. + * @param options.min_id Return results newer than ID. + * @param options.pinned Return statuses which include pinned statuses. + * @param options.exclude_replies Return statuses which exclude replies. + * @param options.exclude_reblogs Return statuses which exclude reblogs. + * @param options.only_media Show only statuses with media attached? Defaults to false. + * @return Account's statuses. + */ + public getAccountStatuses( + id: string, + options?: Partial<{ + max_id: string; + min_id: string; + since_id: string; + limit: number; + only_media: boolean; + pinned: boolean; + exclude_replies: boolean; + exclude_reblogs: boolean; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.only_media) { + params.set("only_media", "true"); + } + if (options.pinned) { + params.set("pinned", "true"); + } + if (options.exclude_replies) { + params.set("exclude_replies", "true"); + } + if (options.exclude_reblogs) { + params.set("exclude_reblogs", "true"); + } + } + + return this.get[]>( + `/api/v1/accounts/${id}/statuses?${params}`, + extra, + ); + } + + /** + * GET /api/v1/lists/:id/accounts + * + * @param id Target list ID. + * @param options.limit Max number of results to return. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @param options.min_id Return results immediately newer than ID. + * @return Array of accounts. + */ + public getAccountsInList( + id: string, + options: Partial<{ + max_id: string; + since_id: string; + min_id: string; + limit: number; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>( + `/api/v1/lists/${id}/accounts?${params}`, + extra, + ); + } + + /** + * GET /api/v1/blocks + * + * @param options.limit Max number of results to return. Defaults to 40. + * @param options.max_id Return results older than ID. + * @param options.min_id Return results immediately newer than ID. + * @return Array of accounts. + */ + public getBlocks( + options?: Partial<{ + max_id: string; + min_id: string; + limit: number; + }>, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>(`/api/v1/blocks?${params}`); + } + + /** + * GET /api/v1/bookmarks + * + * @param options.limit Max number of results to return. Defaults to 40. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @param options.min_id Return results immediately newer than ID. + * @return Array of statuses. + */ + public getBookmarks( + options?: Partial<{ + max_id: string; + min_id: string; + since_id: string; + limit: number; + }>, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>( + `/api/v1/bookmarks?${params}`, + ); + } + + // FIXME: No Conversation schema + /** + * GET /api/v1/conversations + * + * @param options.limit Max number of results to return. Defaults to 20. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @param options.min_id Return results immediately newer than ID. + * @return Array of statuses. + */ + public getConversationTimeline( + id: string, + options?: Partial<{ + max_id: string; + min_id: string; + since_id: string; + limit: number; + }>, + extra?: RequestInit, + ): Promise> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get( + `/api/v1/conversations/${id}/timeline?${params}`, + extra, + ); + } + + /** + * GET /api/v1/domain_blocks + * + * @param options.limit Max number of results to return. Defaults to 40. + * @param options.max_id Return results older than ID. + * @param options.min_id Return results immediately newer than ID. + * @return Array of domain name. + */ + public getDomainBlocks( + options?: Partial<{ + max_id: string; + since_id: string; + limit: number; + }>, + ): Promise> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get(`/api/v1/domain_blocks?${params}`); + } + + /** + * GET /api/v1/emojis/:id + * + * Versia API only. + * @param id The emoji ID. + * @return Emoji. + */ + public getEmoji( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.get>( + `/api/v1/emojis/${id}`, + extra, + ); + } + + /** + * GET /api/v1/endorsements + * + * @param options.limit Max number of results to return. Defaults to 40. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @return Array of accounts. + */ + public getEndorsements( + options?: Partial<{ + max_id: string; + since_id: string; + limit: number; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>( + `/api/v1/endorsements?${params}`, + extra, + ); + } + + /** + * GET /api/v1/favourites + * + * @param options.limit Max number of results to return. Defaults to 40. + * @param options.max_id Return results older than ID. + * @param options.min_id Return results immediately newer than ID. + * @return Array of statuses. + */ + public getFavourites( + options?: Partial<{ + max_id: string; + min_id: string; + limit: number; + }>, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>( + `/api/v1/favourites?${params}`, + ); + } + + // FIXME: No FeaturedTag schema + /** + * GET /api/v1/featured_tags + * + * @return Array of featured tag. + */ + public getFeaturedTags(extra?: RequestInit): Promise> { + return this.get("/api/v1/featured_tags", extra); + } + + // TODO: getFilter + // TODO: getFilters + + /** + * GET /api/v1/follow_requests + * + * @param options.limit Maximum number of results. + * @return Array of account. + */ + public getFollowRequests( + options?: Partial<{ + limit: number; + }>, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>( + `/api/v1/follow_requests?${params}`, + ); + } + + /** + * GET /api/v1/followed_tags + * + * @return Array of Tag. + */ + public getFollowedTags( + extra?: RequestInit, + ): Promise[]>> { + return this.get[]>("/api/v1/followed_tags", extra); + } + + /** + * GET /api/v1/timelines/home + * + * @param options.local Show only local statuses? Defaults to false. + * @param options.limit Max number of results to return. Defaults to 20. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @param options.min_id Return results immediately newer than ID. + * @return Array of statuses. + */ + public getHomeTimeline( + options?: Partial<{ + max_id: string; + min_id: string; + since_id: string; + limit: number; + local: boolean; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.local) { + params.set("local", "true"); + } + } + + return this.get[]>( + `/api/v1/timelines/home?${params}`, + extra, + ); + } + + /** + * GET /api/v1/instance + * + * @return Instance. + */ + public getInstance( + extra?: RequestInit, + ): Promise>> { + return this.get>("/api/v2/instance", extra); + } + + // FIXME: No InstanceActivity schema + /** + * GET /api/v1/instance/activity + */ + public getInstanceActivity( + extra?: RequestInit, + ): Promise> { + return this.get("/api/v1/instance/activity", extra); + } + + // FIXME: No Announcement schema + /** + * GET /api/v1/announcements + * + * @return Array of announcements. + */ + public getInstanceAnnouncements( + extra?: RequestInit, + ): Promise> { + return this.get("/api/v1/instance/announcements", extra); + } + + /** + * GET /api/v1/custom_emojis + * + * Get custom emojis. Returns both server and user emojis. + * @return Array of emojis. + */ + public getInstanceCustomEmojis( + extra?: RequestInit, + ): Promise[]>> { + return this.get[]>( + "/api/v1/custom_emojis", + extra, + ); + } + + /** + * GET /api/v1/directory + * + * @param options.limit How many accounts to load. Default 40. + * @param options.offset How many accounts to skip before returning results. Default 0. + * @param options.order Order of results. + * @param options.local Only return local accounts. + * @return Array of accounts. + */ + public getInstanceDirectory( + options?: Partial<{ + limit: number; + local: boolean; + offset: number; + order: "active" | "new"; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.local) { + params.set("local", "true"); + } + if (options.offset) { + params.set("offset", options.offset.toString()); + } + if (options.order) { + params.set("order", options.order); + } + } + + return this.get[]>( + `/api/v1/directory?${params}`, + extra, + ); + } + + /** + * GET /api/v1/instance/extended_description + * + * @returns Instance's extended description. + */ + public getInstanceExtendedDescription( + extra?: RequestInit, + ): Promise>> { + return this.get>( + "/api/v1/instance/extended_description", + extra, + ); + } + + /** + * GET /api/v1/instance/peers + * + * @return Array of instance domains. + */ + public getInstancePeers(extra?: RequestInit): Promise> { + return this.get("/api/v1/instance/peers", extra); + } + + /** + * GET /api/v1/instance/privacy_policy + * + * Versia API only. + * @returns + */ + public getInstancePrivacyPolicy( + extra?: RequestInit, + ): Promise>> { + return this.get>( + "/api/v1/instance/privacy_policy", + extra, + ); + } + + /** + * GET /api/v1/instance/terms_of_service + * + * @returns + */ + public getInstanceTermsOfService( + extra?: RequestInit, + ): Promise>> { + return this.get>( + "/api/v1/instance/terms_of_service", + extra, + ); + } + + /** + * GET /api/v1/trends + * + * @param options.limit Maximum number of results to return. Defaults to 10. + * @return Array of Tag. + */ + public getInstanceTrends( + options?: Partial<{ limit: number }>, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>(`/api/v1/trends?${params}`); + } + + // FIXME: No List schema + /** + * GET /api/v1/lists/:id + * + * @param id Target list ID. + * @return List. + */ + public getList(id: string, extra?: RequestInit): Promise> { + return this.get(`/api/v1/lists/${id}`, extra); + } + + /** + * GET /api/v1/timelines/list/:id + * + * @param id Local ID of the list in the database. + * @param options.limit Max number of results to return. Defaults to 20. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @param options.min_id Return results immediately newer than ID. + * @return Array of statuses. + */ + public getListTimeline( + id: string, + options?: Partial<{ + max_id: string; + min_id: string; + since_id: string; + limit: number; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>( + `/api/v1/timelines/list/${id}?${params}`, + extra, + ); + } + + // FIXME: No List schema + /** + * GET /api/v1/lists + * + * @return Array of lists. + */ + public getLists(extra?: RequestInit): Promise> { + return this.get("/api/v1/lists", extra); + } + + /** + * GET /api/v1/timelines/public?local=true + * + * @param options.only_media Show only statuses with media attached? Defaults to false. + * @param options.limit Max number of results to return. Defaults to 20. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @param options.min_id Return results immediately newer than ID. + * @param options.only_media Show only statuses with media attached? Defaults to false. + * @return Array of statuses. + */ + public getLocalTimeline( + options?: Partial<{ + max_id: string; + min_id: string; + since_id: string; + limit: number; + only_media: boolean; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + params.set("local", "true"); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.only_media) { + params.set("only_media", "true"); + } + } + + return this.get[]>( + `/api/v1/timelines/public?${params}`, + extra, + ); + } + + /** + * GET /api/v1/markers + * + * @param timelines Array of timeline names, String enum anyOf home, notifications. + * @return Marker or empty object. + */ + public getMarkers( + timelines: ("home" | "notifications")[], + ): Promise | Record>> { + const params = new URLSearchParams(); + + for (const timeline of timelines) { + params.append("timelines[]", timeline); + } + + return this.get | Record>( + `/api/v1/markers?${params}`, + ); + } + + /** + * GET /api/v1/media/:id + * + * @param id Target media ID. + * @return Attachment + */ + public getMedia( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.get>( + `/api/v1/media/${id}`, + extra, + ); + } + + /** + * GET /api/v1/mutes + * + * @param options.limit Max number of results to return. Defaults to 40. + * @param options.max_id Return results older than ID. + * @param options.min_id Return results immediately newer than ID. + * @return Array of accounts. + */ + public getMutes( + options?: Partial<{ + max_id: string; + since_id: string; + limit: number; + }>, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>(`/api/v1/mutes?${params}`); + } + + /** + * GET /api/v1/notifications/:id + * + * @param id Target notification ID. + * @return Notification. + */ + public getNotification( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.get>( + `/api/v1/notifications/${id}`, + extra, + ); + } + + /** + * GET /api/v1/notifications + * + * @param options.limit Max number of results to return. Defaults to 20. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @param options.min_id Return results immediately newer than ID. + * @param options.exclude_types Array of types to exclude. + * @param options.account_id Return only notifications received from this account. + * @return Array of notifications. + */ + public getNotifications( + options?: Partial<{ + max_id: string; + min_id: string; + since_id: string; + limit: number; + exclude_types: string[]; + account_id: string; + }>, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.exclude_types) { + for (const type of options.exclude_types) { + params.append("exclude_types[]", type); + } + } + if (options.account_id) { + params.set("account_id", options.account_id); + } + } + + return this.get[]>( + `/api/v1/notifications?${params}`, + ); + } + + /** + * GET /api/v1/polls/:id + * + * @param id Target poll ID. + * @return Poll. + */ + public getPoll( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.get>(`/api/v1/polls/${id}`, extra); + } + + /** + * GET /api/v1/preferences + * + * @return Preferences. + */ + public getPreferences( + extra?: RequestInit, + ): Promise>> { + return this.get>( + "/api/v1/preferences", + extra, + ); + } + + /** + * GET /api/v1/timelines/public + * + * @param options.only_media Show only statuses with media attached? Defaults to false. + * @param options.limit Max number of results to return. Defaults to 20. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @param options.min_id Return results immediately newer than ID. + * @return Array of statuses. + */ + public getPublicTimeline( + options?: Partial<{ + max_id: string; + min_id: string; + since_id: string; + limit: number; + only_media: boolean; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.only_media) { + params.set("only_media", "true"); + } + } + + return this.get[]>( + `/api/v1/timelines/public?${params}`, + extra, + ); + } + + /** + * GET /api/v1/push/subscription + * + * @return PushSubscription. + */ + public getPushSubscription( + extra?: RequestInit, + ): Promise>> { + return this.get>( + "/api/v1/push/subscription", + extra, + ); + } + + /** + * GET /api/v1/accounts/relationships + * + * @param id The account ID. + * @param options.with_suspended Include relationships with suspended accounts? Defaults to false. + * @return Relationship + */ + public getRelationship( + id: string, + options?: Partial<{ + with_suspended: boolean; + }>, + extra?: RequestInit, + ): Promise>> { + return this.getRelationships([id], options, extra).then((r) => ({ + ...r, + data: r.data[0] as z.infer, + })); + } + + /** + * GET /api/v1/accounts/relationships + * + * @param ids Array of account IDs. + * @param options.with_suspended Include relationships with suspended accounts? Defaults to false. + * @return Array of Relationship. + */ + public getRelationships( + ids: string[], + options?: Partial<{ + with_suspended: boolean; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + for (const id of ids) { + params.append("id[]", id); + } + + if (options) { + if (options.with_suspended) { + params.set("with_suspended", "true"); + } + } + + return this.get[]>( + `/api/v1/accounts/relationships?${params}`, + extra, + ); + } + + /** + * GET /api/v1/roles/:id + * + * Versia API only. + * @param id Target role ID. + * @return Role. + */ + public getRole( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.get>(`/api/v1/roles/${id}`, extra); + } + + /** + * GET /api/v1/roles + * + * Versia API only. + * @returns Array of roles. + */ + public getRoles( + extra?: RequestInit, + ): Promise[]>> { + return this.get[]>("/api/v1/roles", extra); + } + + // FIXME: No ScheduledStatus schema + /** + * GET /api/v1/scheduled_statuses/:id + * + * @param id Target status ID. + * @return ScheduledStatus. + */ + public getScheduledStatus( + id: string, + extra?: RequestInit, + ): Promise> { + return this.get(`/api/v1/scheduled_statuses/${id}`, extra); + } + + // FIXME: No ScheduledStatus schema + /** + * GET /api/v1/scheduled_statuses + * + * @param options.limit Max number of results to return. Defaults to 20. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @param options.min_id Return results immediately newer than ID. + * @return Array of scheduled statuses. + */ + public getScheduledStatuses( + options?: Partial<{ + max_id: string; + min_id: string; + since_id: string; + limit: number; + }>, + extra?: RequestInit, + ): Promise> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get( + `/api/v1/scheduled_statuses?${params}`, + extra, + ); + } + + /** + * GET /api/v1/statuses/:id + * + * @param id The target status id. + * @return Status + */ + public getStatus( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.get>( + `/api/v1/statuses/${id}`, + extra, + ); + } + + /** + * GET /api/v1/statuses/:id/context + * + * Get parent and child statuses. + * @param id The target status id. + * @param options.limit Max number of results to return. Defaults to 20. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @return Context + */ + public getStatusContext( + id: string, + options?: Partial<{ + limit: number; + max_id: string; + since_id: string; + }>, + extra?: RequestInit, + ): Promise>> { + const params = new URLSearchParams(); + + if (options) { + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + } + + return this.get>( + `/api/v1/statuses/${id}/context?${params}`, + extra, + ); + } + + /** + * GET /api/v1/statuses/:id/favourited_by + * + * @param id The target status id. + * @param options.limit Max number of results to return. Defaults to 40. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @return Array of accounts. + */ + public getStatusFavouritedBy( + id: string, + options?: Partial<{ + max_id: string; + since_id: string; + limit: number; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>( + `/api/v1/statuses/${id}/favourited_by?${params}`, + extra, + ); + } + + /** + * GET /api/v1/statuses/:id/reblogged_by + * + * @param id The target status id. + * @param options.limit Max number of results to return. Defaults to 40. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @return Array of accounts. + */ + public getStatusRebloggedBy( + id: string, + options?: Partial<{ + max_id: string; + since_id: string; + limit: number; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>( + `/api/v1/statuses/${id}/reblogged_by?${params}`, + extra, + ); + } + + /** + * GET /api/v1/statuses/:id/source + * + * Obtain the source properties for a status so that it can be edited. + * @param id The target status id. + * @return StatusSource + */ + public getStatusSource( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.get>( + `/api/v1/statuses/${id}/source`, + extra, + ); + } + + /** + * GET /api/v1/featured_tags/suggestions + * + * @return Array of tag. + */ + public getSuggestedTags( + extra?: RequestInit, + ): Promise[]>> { + return this.get[]>( + "/api/v1/featured_tags/suggestions", + extra, + ); + } + + /** + * GET /api/v1/suggestions + * + * @param options.limit Maximum number of results. + * @return Array of accounts. + */ + public getSuggestions( + options?: Partial<{ limit: number }>, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.limit) { + params.set("limit", options.limit.toString()); + } + } + + return this.get[]>( + `/api/v1/suggestions?${params}`, + ); + } + + /** + * GET /api/v1/tags/:id + * + * @param id Target hashtag id. + * @return Tag + */ + public getTag( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.get>(`/api/v1/tags/${id}`, extra); + } + + /** + * GET /api/v1/timelines/tag/:hashtag + * + * @param hashtag Content of a #hashtag, not including # symbol. + * @param options.local Show only local statuses? Defaults to false. + * @param options.only_media Show only statuses with media attached? Defaults to false. + * @param options.limit Max number of results to return. Defaults to 20. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @param options.min_id Return results immediately newer than ID. + * @return Array of statuses. + */ + public getTagTimeline( + id: string, + options?: Partial<{ + max_id: string; + min_id: string; + since_id: string; + limit: number; + local: boolean; + only_media: boolean; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + if (options) { + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.local) { + params.set("local", "true"); + } + if (options.only_media) { + params.set("only_media", "true"); + } + } + + return this.get[]>( + `/api/v1/timelines/tag/${id}?${params}`, + extra, + ); + } + + // TODO: listStreaming + // TODO: localStreaming + + /** + * GET /api/v1/accounts/lookup + * + * @param acct The username or Webfinger address to lookup. + * @return Account. + */ + public lookupAccount( + acct: string, + extra?: RequestInit, + ): Promise>> { + const params = new URLSearchParams(); + + params.set("acct", acct); + + return this.get>( + `/api/v1/accounts/lookup?${params}`, + extra, + ); + } + + /** + * POST /api/v1/accounts/:id/mute + * + * @param id The account ID. + * @param options.notifications Mute notifications in addition to statuses. + * @param options.duration Duration of mute in seconds. Defaults to indefinite. + * @return Relationship + */ + public muteAccount( + id: string, + options?: Partial<{ notifications: boolean; duration: number }>, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/accounts/${id}/mute`, + options, + extra, + ); + } + + /** + * POST /api/v1/statuses/:id/mute + * + * @param id The target status id. + * @return Status + */ + public muteStatus( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/statuses/${id}/mute`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/accounts/:id/pin + * + * @param id The account ID. + * @return Relationship + */ + public pinAccount( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/accounts/${id}/pin`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/statuses/:id/pin + * @param id The target status id. + * @return Status + */ + public pinStatus( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/statuses/${id}/pin`, + undefined, + extra, + ); + } + + // FIXME: No ScheduledStatus schema + /** + * POST /api/v1/statuses + * + * @param status Text content of status. + * @param options.media_ids Array of Attachment ids. + * @param options.poll Poll object. + * @param options.in_reply_to_id ID of the status being replied to, if status is a reply. + * @param options.quote_id ID of the status being quoted to, if status is a quote. + * @param options.sensitive Mark status and attached media as sensitive? + * @param options.spoiler_text Text to be shown as a warning or subject before the actual content. + * @param options.visibility Visibility of the posted status. + * @param options.content_type Content type of the status (MIME format). + * @param options.language ISO 639 language code for this status. + * @param options.scheduled_at ISO 8601 Datetime at which to schedule a status. + * @param options.local_only Post status to local timeline only? + * @return Status. When options.scheduled_at is present, ScheduledStatus is returned instead. + */ + public postStatus( + status: string, + options: { + in_reply_to_id?: string; + quote_id?: string; + media_ids?: string[]; + sensitive?: boolean; + spoiler_text?: string; + visibility?: z.infer; + content_type?: StatusContentType; + scheduled_at?: string; + language?: string; + local_only?: boolean; + poll?: { + expires_in?: number; + hide_totals?: boolean; + multiple?: boolean; + options: string[]; + }; + }, + extra?: RequestInit, + ): Promise | unknown>> { + return this.post | unknown>( + "/api/v1/statuses", + { status, ...options }, + extra, + ); + } + + // TODO: publicStreaming + // FIXME: No Conversation schema + /** + * POST /api/v1/conversations/:id/read + * + * @param id Target conversation ID. + * @return Conversation. + */ + public readConversation( + id: string, + extra?: RequestInit, + ): Promise> { + return this.post( + `/api/v1/conversations/${id}/read`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/statuses/:id/reblog + * + * @param id The target status id. + * @param options.visibility Visibility of the reblogged status. + * @return Status. + */ + public reblogStatus( + id: string, + options?: Partial<{ + visibility: z.infer; + }>, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/statuses/${id}/reblog`, + options, + extra, + ); + } + + /** + * POST /api/v1/accounts/:id/refetch + * + * Starts a refetch of an account from a remote source. + * Versia API only. + * @param id The account ID. + * @return Account with updated data. + */ + public refetchAccount( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/accounts/${id}/refetch`, + undefined, + extra, + ); + } + + /** + * POST /oauth/token + * + * Revoke an OAuth token. + * @param client_id will be generated by #createApp or #registerApp + * @param client_secret will be generated by #createApp or #registerApp + * @param token will be get #fetchAccessToken + */ + public refreshToken( + client_id: string, + client_secret: string, + refresh_token: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + "/oauth/token", + { + client_id, + client_secret, + grant_type: "refresh_token", + refresh_token, + }, + extra, + ); + } + + /** + * POST /api/v1/accounts + * + * @param username Username for the account. + * @param email Email for the account. + * @param password Password for the account. + * @param agreement Whether the user agrees to the local rules, terms, and policies. + * @param locale The language of the confirmation email that will be sent + * @param reason Text that will be reviewed by moderators if registrations require manual approval. + * @return An account token. + */ + public registerAccount( + username: string, + email: string, + password: string, + agreement: boolean, + locale: string, + reason: string, + extra?: RequestInit, + ): Promise>> { + return this.postForm>( + "/api/v1/accounts", + { username, email, password, agreement, locale, reason }, + extra, + ); + } + + /** + * POST /api/v1/follow_requests/:id/reject + * + * @param id Target account ID. + * @return Relationship. + */ + public rejectFollowRequest( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/follow_requests/${id}/reject`, + undefined, + extra, + ); + } + + // FIXME: Announcement schema is not defined. + /** + * DELETE /api/v1/announcements/:id/reactions/:name + * + * @param id The ID of the Announcement in the database. + * @param name Unicode emoji, or the shortcode of a custom emoji. + */ + public removeReactionFromAnnouncement( + id: string, + name: string, + extra?: RequestInit, + ): Promise> { + return this.delete( + `/api/v1/announcements/${id}/reactions/${name}`, + undefined, + extra, + ); + } + + /** + * DELETE /api/v1/roles/:roleId + * + * Versia API only. + * @param roleId Role ID to remove from requesting account. + * @returns + */ + public removeRole( + role_id: string, + extra?: RequestInit, + ): Promise> { + return this.delete(`/api/v1/roles/${role_id}`, undefined, extra); + } + + /** + * POST /api/v1/reports + * + * @param account_id Target account ID. + * @param options.status_ids Array of Statuses ids to attach to the report. + * @param options.comment The reason for the report. Default maximum of 1000 characters. + * @param options.forward If the account is remote, should the report be forwarded to the remote admin? + * @param options.category Specify if the report is due to spam, violation of enumerated instance rules, or some other reason. Defaults to other. Will be set to violation if rule_ids[] is provided (regardless of any category value you provide). + * @param options.rule_ids For violation category reports, specify the ID of the exact rules broken. Rules and their IDs are available via GET /api/v1/instance/rules and GET /api/v1/instance. + * @return Report. + */ + public report( + account_id: string, + options: { + status_ids?: string[]; + rule_ids?: string[]; + comment: string; + forward?: boolean; + category?: z.infer; + }, + extra?: RequestInit, + ): Promise>> { + return this.post>( + "/api/v1/reports", + { account_id, ...options }, + extra, + ); + } + + /** + * POST /oauth/revoke + * + * Revoke an OAuth token. + * @param client_id will be generated by #createApp or #registerApp + * @param client_secret will be generated by #createApp or #registerApp + * @param token will be get #fetchAccessToken + */ + public revokeToken( + client_id: string, + client_secret: string, + token: string, + extra?: RequestInit, + ): Promise> { + return this.post( + "/oauth/revoke", + { client_id, client_secret, token }, + extra, + ); + } + + /** + * POST /api/v1/markers + * + * @param options.home Marker position of the last read status ID in home timeline. + * @param options.notifications Marker position of the last read notification ID in notifications. + * @return Marker. + */ + public saveMarkers( + options: Partial<{ + home: { + last_read_id: string; + }; + notifications: { + last_read_id: string; + }; + }>, + extra?: RequestInit, + ): Promise>> { + return this.post>( + "/api/v1/markers", + options, + extra, + ); + } + + // FIXME: No ScheduledStatus schema + /** + * PUT /api/v1/scheduled_statuses/:id + * + * @param id Target scheduled status ID. + * @param scheduled_at ISO 8601 Datetime at which the status will be published. + * @return ScheduledStatus. + */ + public scheduleStatus( + id: string, + scheduled_at?: Date, + extra?: RequestInit, + ): Promise> { + return this.put( + `/api/v1/scheduled_statuses/${id}`, + { scheduled_at: scheduled_at?.toISOString() }, + extra, + ); + } + + /** + * GET /api/v2/search + * + * @param q The search query. + * @param type Enum of search target. + * @param options.limit Maximum number of results to load, per type. Defaults to 20. Max 40. + * @param options.max_id Return results older than this id. + * @param options.min_id Return results immediately newer than this id. + * @param options.resolve Attempt WebFinger lookup. Defaults to false. + * @param options.following Only include accounts that the user is following. Defaults to false. + * @param options.account_id If provided, statuses returned will be authored only by this account. + * @param options.exclude_unreviewed Filter out unreviewed tags? Defaults to false. + * @return Results. + */ + public search( + q: string, + options: Partial<{ + account_id: string; + exclude_unreviewed: boolean; + following: boolean; + limit: number; + max_id: string; + min_id: string; + offset: number; + resolve: boolean; + type: "accounts" | "hashtags" | "statuses"; + }>, + extra?: RequestInit, + ): Promise>> { + const params = new URLSearchParams(); + + params.set("q", q); + + if (options) { + if (options.account_id) { + params.set("account_id", options.account_id); + } + if (options.exclude_unreviewed) { + params.set("exclude_unreviewed", "true"); + } + if (options.following) { + params.set("following", "true"); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.offset) { + params.set("offset", options.offset.toString()); + } + if (options.resolve) { + params.set("resolve", "true"); + } + if (options.type) { + params.set("type", options.type); + } + } + + return this.get>( + `/api/v2/search?${params}`, + extra, + ); + } + + /** + * GET /api/v1/accounts/search + * + * @param q Search query. + * @param options.limit Max number of results to return. Defaults to 40. + * @param options.max_id Return results older than ID. + * @param options.since_id Return results newer than ID. + * @param options.following Limit search to accounts the current user is following. + * @param options.resolve Resolve non-local accounts? + * @return The array of accounts. + */ + public searchAccount( + q: string, + options: Partial<{ + following: boolean; + limit: number; + max_id: string; + resolve: boolean; + since_id: string; + }>, + extra?: RequestInit, + ): Promise[]>> { + const params = new URLSearchParams(); + + params.set("q", q); + + if (options) { + if (options.following) { + params.set("following", "true"); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.resolve) { + params.set("resolve", "true"); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + } + + return this.get[]>( + `/api/v1/accounts/search?${params}`, + extra, + ); + } + + // TODO: streamingURL + + /** + * POST /api/v1/push/subscription + * + * @param subscription.endpoint Endpoint URL that is called when a notification event occurs. + * @param subscription.keys.p256dh User agent public key. Base64 encoded string of public key of ECDH key using prime256v1 curve. + * @param subscription.keys Auth secret. Base64 encoded string of 16 bytes of random data. + * @param data.alerts.follow Receive follow notifications? + * @param data.alerts.favourite Receive favourite notifications? + * @param data.alerts.reblog Receive reblog notifictaions? + * @param data.alerts.mention Receive mention notifications? + * @param data.alerts.poll Receive poll notifications? + * @param data.alerts.status Receive status notifications? + * @param data.alerts.follow_request Receive follow request notifications? + * @param data.alerts.update Receive status update notifications? + * @param data.alerts.admin.sign_up Receive sign up notifications? + * @param data.alerts.admin.report Receive report notifications? + * @param data.policy Notification policy. Defaults to all. + * @return PushSubscription. + */ + public subscribePushNotifications( + subscription: { + endpoint: string; + keys: { + auth: string; + p256dh: string; + }; + }, + data?: { + alerts: Partial<{ + favourite: boolean; + follow: boolean; + mention: boolean; + poll: boolean; + reblog: boolean; + status: boolean; + follow_request: boolean; + update: boolean; + "admin.sign_up": boolean; + "admin.report": boolean; + }>; + policy?: "all" | "followed" | "follower" | "none"; + }, + extra?: RequestInit, + ): Promise>> { + return this.post>( + "/api/v1/push/subscription", + { subscription, data }, + extra, + ); + } + + // TODO: tagStreaming + + /** + * DELETE /api/v1/accounts/:account_id/roles/:role_id + * + * Versia API only. + * @param account_id Account ID to remove the role from. + * @param role_id Role ID to remove from the account. + * @returns No content. + */ + public unassignRole( + account_id: string, + role_id: string, + extra?: RequestInit, + ): Promise> { + return this.delete( + `/api/v1/accounts/${account_id}/roles/${role_id}`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/accounts/:id/unblock + * + * @param id The account ID. + * @return Relationship + */ + public unblockAccount( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/accounts/${id}/unblock`, + undefined, + extra, + ); + } + + /** + * DELETE /api/v1/domain_blocks + * + * @param domain Domain to unblock + */ + public unblockDomain( + domain: string, + extra?: RequestInit, + ): Promise> { + return this.delete("/api/v1/domain_blocks", { domain }, extra); + } + + /** + * POST /api/v1/statuses/:id/unbookmark + * + * @param id The target status id. + * @return Status. + */ + public unbookmarkStatus( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/statuses/${id}/unbookmark`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/statuses/:id/unfavourite + * + * @param id The target status id. + * @return Status. + */ + public unfavouriteStatus( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/statuses/${id}/unfavourite`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/accounts/:id/unfollow + * + * @param id The account ID. + * @return Relationship + */ + public unfollowAccount( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/accounts/${id}/unfollow`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/accounts/:id/unmute + * + * @param id The account ID. + * @return Relationship + */ + public unmuteAccount( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/accounts/${id}/unmute`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/accounts/:id/unpin + * + * @param id The account ID. + * @return Relationship + */ + public unpinAccount( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/accounts/${id}/unpin`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/statuses/:id/unpin + * + * @param id The target status id. + * @return Status + */ + public unpinStatus( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/statuses/${id}/unpin`, + undefined, + extra, + ); + } + + /** + * POST /api/v1/statuses/:id/unreblog + * + * @param id The target status id. + * @return Status. + */ + public unreblogStatus( + id: string, + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/statuses/${id}/unreblog`, + undefined, + extra, + ); + } + + /** + * PATCH /api/v1/accounts/update_credentials + * + * @param options.avatar Avatar image, as a File + * @param options.bot Is this account a bot? + * @param options.discoverable Should this account be included in directory? + * @param options.display_name Account display name. + * @param options.fields_attributes Array of profile metadata. + * @param options.header Header image, as a File + * @param options.locked Is this account locked? + * @param options.note Brief account description. + * @param options.source Source metadata. + * @return An account. + */ + public updateCredentials( + options: Partial<{ + avatar: File; + bot: boolean; + discoverable: boolean; + display_name: string; + fields_attributes: { + name: string; + value: string; + }[]; + header: File; + locked: boolean; + note: string; + source: Partial<{ + language: string; + privacy: string; + sensitive: boolean; + }>; + }>, + extra?: RequestInit, + ): Promise>> { + return this.patchForm>( + "/api/v1/accounts/update_credentials", + options, + extra, + ); + } + + /** + * PATCH /api/v1/emojis/:id + * + * Versia API only. + * @param id Target emoji ID. + * @param options.shortcode Emoji shortcode. + * @param options.image Emoji image, as a File, or a URL. + * @param options.category Emoji category. + * @param options.alt Emoji description. + * @param options.global Whether the emoji should be visible to all users (requires `emoji` permission). + * @return Emoji. + */ + public updateEmoji( + id: string, + options: Partial<{ + alt: string; + category: string; + global: boolean; + image: File | URL; + shortcode: string; + }>, + extra?: RequestInit, + ): Promise>> { + return this.patchForm>( + `/api/v1/emojis/${id}`, + { + ...options, + image: undefined, + element: + options.image instanceof File + ? options.image + : options.image?.toString(), + }, + extra, + ); + } + + // TODO: updateFilter + // FIXME: No List schema + /** + * PUT /api/v1/lists/:id + * + * @param id Target list ID. + * @param options.title New list title. + * @param options.replies_policy Which replies should be shown in the list. + * @param options.exclusive Should the members of this list be removed from the home timeline? + * @return List. + */ + public updateList( + id: string, + options: { + title: string; + replies_policy?: "none" | "followed" | "list"; + exclusive?: boolean; + }, + extra?: RequestInit, + ): Promise> { + return this.put(`/api/v1/lists/${id}`, options, extra); + } + + /** + * PUT /api/v1/media/:id + * + * @param id Target media ID. + * @param options.file The file to be attached, using multipart form data. + * @param options.description A plain-text description of the media. + * @param options.focus Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0. + * @param options.is_sensitive Whether the media is sensitive. + * @return Attachment + */ + public updateMedia( + id: string, + options?: Partial<{ + description: string; + file: File; + focus: string; + is_sensitive: boolean; + }>, + extra?: RequestInit, + ): Promise>> { + return this.putForm>( + `/api/v1/media/${id}`, + { ...options }, + extra, + ); + } + + /** + * PUT /api/v1/push/subscription + * + * @param data.alerts.follow Receive follow notifications? + * @param data.alerts.favourite Receive favourite notifications? + * @param data.alerts.reblog Receive reblog notifictaions? + * @param data.alerts.mention Receive mention notifications? + * @param data.alerts.poll Receive poll notifications? + * @param data.alerts.status Receive status notifications? + * @param data.alerts.follow_request Receive follow request notifications? + * @param data.alerts.update Receive status update notifications? + * @param data.alerts.admin.sign_up Receive sign up notifications? + * @param data.alerts.admin.report Receive report notifications? + * @param policy Notification policy. Defaults to all. + * @return PushSubscription. + */ + public updatePushSubscription( + data?: { + alerts: Partial<{ + favourite: boolean; + follow: boolean; + mention: boolean; + poll: boolean; + reblog: boolean; + status: boolean; + follow_request: boolean; + update: boolean; + "admin.sign_up": boolean; + "admin.report": boolean; + }>; + }, + policy?: "all" | "followed" | "follower" | "none", + extra?: RequestInit, + ): Promise>> { + return this.put>( + "/api/v1/push/subscription", + { ...data, policy }, + extra, + ); + } + + /** + * PATCH /api/v1/roles/:id + * + * Versia API only. + * @param id Role ID to update. + * @param options.name New role name. + * @param options.permissions New role permissions. + * @param options.priority New role priority. + * @param options.description New role description. + * @param options.visible New role visibility. + * @param options.icon New role icon. + * @returns New role data. + */ + public updateRole( + id: string, + options: Partial<{ + name: string; + permissions: string[]; + priority: number; + description: string; + visible: boolean; + icon: string; + }>, + extra?: RequestInit, + ): Promise>> { + return this.patch>( + `/api/v1/roles/${id}`, + options, + extra, + ); + } + + /** + * POST /api/v1/emojis + * + * Versia API only. + * @param shortcode The shortcode of the emoji. + * @param image The image file to be uploaded, as a File or URL. + * @param options.category A category for the emoji. + * @param options.alt Text description of the emoji. + * @param options.global Whether the emoji should be visible to all users (requires `emoji` permission). + * @return Emoji + */ + public uploadEmoji( + shortcode: string, + image: File | URL, + options?: Partial<{ + alt: string; + category: string; + global: boolean; + }>, + extra?: RequestInit, + ): Promise>> { + return this.postForm>( + "/api/v1/emojis", + { + shortcode, + element: image instanceof File ? image : image.toString(), + ...options, + }, + extra, + ); + } + + // FIXME: No AsyncAttachment schema + /** + * POST /api/v2/media + * + * @param file The file to be attached, using multipart form data. + * @param options.description A plain-text description of the media. + * @param options.focus Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0. + * @return Attachment + */ + public uploadMedia( + file: File, + options?: Partial<{ + description: string; + focus: string; + }>, + extra?: RequestInit, + ): Promise | unknown>> { + return this.postForm | unknown>( + "/api/v2/media", + { file, ...options }, + extra, + ); + } + + // TODO: userStreaming + + /** + * GET /api/v1/accounts/verify_credentials + * + * @return Account. + */ + public verifyAccountCredentials( + extra?: RequestInit, + ): Promise>> { + return this.get>( + "/api/v1/accounts/verify_credentials", + extra, + ); + } + + /** + * GET /api/v1/apps/verify_credentials + * + * @return An Application + */ + public verifyAppCredentials( + extra?: RequestInit, + ): Promise>> { + return this.get>( + "/api/v1/apps/verify_credentials", + extra, + ); + } + + /** + * POST /api/v1/polls/:id/votes + * + * @param id Target poll ID. + * @param choices Array of own votes containing index for each option (starting from 0). + * @return Poll + */ + public votePoll( + id: string, + choices: number[], + extra?: RequestInit, + ): Promise>> { + return this.post>( + `/api/v1/polls/${id}/votes`, + { choices }, + extra, + ); + } +} diff --git a/packages/client/versia/constants.ts b/packages/client/versia/constants.ts new file mode 100644 index 00000000..fb16591f --- /dev/null +++ b/packages/client/versia/constants.ts @@ -0,0 +1,5 @@ +import pkg from "../package.json" with { type: "json" }; + +export const NO_REDIRECT = "urn:ietf:wg:oauth:2.0:oob"; +export const DEFAULT_SCOPE = ["read", "write", "follow"]; +export const DEFAULT_UA = `VersiaClient/${pkg.version} (+${pkg.homepage})`; diff --git a/plugins/openid/routes/oauth/callback.ts b/plugins/openid/routes/oauth/callback.ts index 40687c64..9a7308f6 100644 --- a/plugins/openid/routes/oauth/callback.ts +++ b/plugins/openid/routes/oauth/callback.ts @@ -1,12 +1,12 @@ import { randomString } from "@/math.ts"; import { createRoute, z } from "@hono/zod-openapi"; +import { Account as AccountSchema } from "@versia/client-ng/schemas"; import { Media, Token, User, db } from "@versia/kit/db"; import { type SQL, and, eq, isNull } from "@versia/kit/drizzle"; import { OpenIdAccounts, RolePermissions, Users } from "@versia/kit/tables"; import { setCookie } from "hono/cookie"; import { SignJWT } from "jose"; import { ApiError } from "~/classes/errors/api-error.ts"; -import { Account as AccountSchema } from "~/classes/schemas/account.ts"; import type { PluginType } from "../../index.ts"; import { automaticOidcFlow } from "../../utils.ts";