From 4e9542d058fbf2eaf7c8454b068034aece13c05c Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Tue, 22 Mar 2022 18:06:52 +0000 Subject: [PATCH] feat: switch to new revolt-api --- README.md | 8 +- package.json | 4 +- src/Client.ts | 183 +++---- src/api/routes.ts | 853 --------------------------------- src/index.ts | 1 + src/maps/Bots.ts | 37 +- src/maps/Channels.ts | 151 +++--- src/maps/Members.ts | 39 +- src/maps/Messages.ts | 27 +- src/maps/Servers.ts | 137 +++--- src/maps/Users.ts | 99 ++-- src/tester.ts | 28 +- src/util/Unreads.ts | 2 +- src/websocket/client.ts | 2 +- src/websocket/notifications.ts | 26 +- yarn.lock | 89 +++- 16 files changed, 392 insertions(+), 1294 deletions(-) delete mode 100644 src/api/routes.ts diff --git a/README.md b/README.md index 58a2b3dc..432113ed 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ## Example Usage (Javascript / ES6) ```javascript -import { Client } from "revolt.js"; +import { Client } from "revolt-api"; let client = new Client(); @@ -31,7 +31,7 @@ For example, `node --experimental-specifier-resolution=node index.js`. ## Example Usage (Typescript) ```typescript -import { Client } from "revolt.js"; +import { Client } from "revolt-api"; let client = new Client(); @@ -63,3 +63,7 @@ client.once('ready', () => { }); }); ``` + +## Revolt API Types + +All `revolt-api` types are re-exported from this library. diff --git a/package.json b/package.json index 4852e8e9..eea84252 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "revolt.js", - "version": "5.2.8", + "version": "6.0.0-rc.0", "main": "dist/index.js", "repository": "https://github.com/revoltchat/revolt.js", "author": "Paul Makles ", @@ -16,7 +16,7 @@ "lodash.isequal": "^4.5.0", "long": "^5.2.0", "mobx": "^6.3.2", - "revolt-api": "0.5.3-alpha.12", + "revolt-api": "0.5.3-rc.7", "ulid": "^2.3.0", "ws": "^8.2.2" }, diff --git a/src/Client.ts b/src/Client.ts index 6940071f..6a56175e 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -1,13 +1,9 @@ -import Axios, { AxiosInstance, AxiosRequestConfig } from "axios"; import EventEmitter from "eventemitter3"; import defaultsDeep from "lodash.defaultsdeep"; import { action, makeObservable, observable } from "mobx"; -import type { Session } from "revolt-api/types/Auth"; -import type { AttachmentMetadata, SizeOptions } from "revolt-api/types/Autumn"; -import type { RevoltConfiguration } from "revolt-api/types/Core"; -import { MemberCompositeKey, Role } from "revolt-api/types/Servers"; - -import { Route, RoutePath, RouteMethod } from "./api/routes"; +import type { DataCreateAccount, DataLogin, DataOnboard, Session } from "revolt-api"; +import type { RevoltConfig, Metadata } from "revolt-api"; +import { API, MemberCompositeKey, Role } from "revolt-api"; import Bots from "./maps/Bots"; import Channels, { Channel } from "./maps/Channels"; @@ -102,7 +98,12 @@ export const RE_MENTIONS = /<@([A-z0-9]{26})>/g; export const RE_SPOILER = /!!.+!!/g; export type FileArgs = [ - options?: SizeOptions, + options?: { + max_side?: number, + size?: number, + width?: number, + height?: number + }, allowAnimation?: boolean, fallback?: string, ]; @@ -110,13 +111,13 @@ export type FileArgs = [ export class Client extends EventEmitter { heartbeat: number; + api: API; session?: Session | string; user: Nullable = null; options: ClientOptions; websocket: WebSocketClient; - private Axios: AxiosInstance; - configuration?: RevoltConfiguration; + configuration?: RevoltConfig; users: Users; channels: Channels; @@ -160,103 +161,36 @@ export class Client extends EventEmitter { this.unreads = new Unreads(this); } - this.Axios = Axios.create({ baseURL: this.apiURL }); + this.api = new API({ baseURL: this.apiURL }); this.websocket = new WebSocketClient(this); this.heartbeat = this.options.heartbeat; - - if (options.debug) { - this.Axios.interceptors.request.use((request) => { - console.debug( - "[<]", - request.method?.toUpperCase(), - request.url, - ); - return request; - }); - - this.Axios.interceptors.response.use((response) => { - console.debug( - "[>] (" + response.status + ":", - response.statusText + ")", - JSON.stringify(response.data), - ); - return response; - }); - } } /** * ? Configuration. */ + /** + * Get the current API base URL. + */ get apiURL() { return this.options.apiURL; } + /** + * Whether debug mode is turned on. + */ get debug() { return this.options.debug; } + /** + * Whether revolt.js should auto-reconnect + */ get autoReconnect() { return this.options.autoReconnect; } - /** - * ? Axios request wrapper. - */ - - req( - method: M, - url: T, - ): Promise["response"]>; - req( - method: M, - url: T, - data: Route["data"], - ): Promise["response"]>; - - /** - * Perform an HTTP request using Axios, specifying a route data object. - * @param method HTTP method - * @param url Target route - * @param data Route data object - * @returns The response body - */ - async req( - method: M, - url: T, - data?: Route["data"], - ): Promise["response"]> { - const res = await this.Axios.request({ - method, - data, - url, - }); - - return res.data; - } - - /** - * Perform an HTTP request using Axios, specifying a request config. - * @param method HTTP method - * @param url Target route - * @param data Axios request config object - * @returns The response body - */ - async request( - method: M, - url: T, - data: AxiosRequestConfig, - ): Promise["response"]> { - const res = await this.Axios.request({ - ...data, - method, - url, - }); - - return res.data; - } - /** * ? Authentication and connection. */ @@ -269,7 +203,7 @@ export class Client extends EventEmitter { * configuration if it has already been fetched before. */ async connect() { - this.configuration = await this.req("GET", "/"); + this.configuration = await this.api.get("/"); } /** @@ -279,22 +213,16 @@ export class Client extends EventEmitter { if (!this.configuration) await this.connect(); } - private $generateHeaders( - session: Session | string | undefined = this.session, - ) { - if (typeof session === "string") { - return { - "x-bot-token": session, - }; - } else { - return { - "x-session-token": session?.token, - }; - } - } - + /** + * Update API object to use authentication. + */ private $updateHeaders() { - this.Axios.defaults.headers = this.$generateHeaders(); + this.api = new API({ + baseURL: this.apiURL, + authentication: { + revolt: this.session + } + }); } /** @@ -302,11 +230,15 @@ export class Client extends EventEmitter { * @param details Login data object * @returns An onboarding function if onboarding is required, undefined otherwise */ - async login(details: Route<"POST", "/auth/session/login">["data"]) { + async login(details: DataLogin) { await this.fetchConfiguration(); - this.session = await this.req("POST", "/auth/session/login", details); - - return await this.$connect(); + const data = await this.api.post("/auth/session/login", details); + if (data.result === 'Success') { + this.session = data; + return await this.$connect(); + } else { + throw "MFA not implemented!"; + } } /** @@ -337,7 +269,7 @@ export class Client extends EventEmitter { */ private async $connect() { this.$updateHeaders(); - const { onboarding } = await this.req("GET", "/onboard/hello"); + const { onboarding } = await this.api.get("/onboard/hello"); if (onboarding) { return (username: string, loginAfterSuccess?: boolean) => this.completeOnboarding({ username }, loginAfterSuccess); @@ -352,10 +284,10 @@ export class Client extends EventEmitter { * @param loginAfterSuccess Defines whether to automatically log in and connect after onboarding finishes */ async completeOnboarding( - data: Route<"POST", "/onboard/complete">["data"], + data: DataOnboard, loginAfterSuccess?: boolean, ) { - await this.req("POST", "/onboard/complete", data); + await this.api.post("/onboard/complete", data); if (loginAfterSuccess) { await this.websocket.connect(); } @@ -371,7 +303,7 @@ export class Client extends EventEmitter { * @returns Invite information. */ async fetchInvite(code: string) { - return await this.req("GET", `/invites/${code}` as "/invites/id"); + return await this.api.get(`/invites/${code}`); } /** @@ -380,7 +312,7 @@ export class Client extends EventEmitter { * @returns Data provided by invite. */ async joinInvite(code: string) { - return await this.req("POST", `/invites/${code}` as "/invites/id"); + return await this.api.post(`/invites/${code}`); } /** @@ -388,7 +320,7 @@ export class Client extends EventEmitter { * @param code The invite code. */ async deleteInvite(code: string) { - await this.req("DELETE", `/invites/${code}` as "/invites/id"); + await this.api.delete(`/invites/${code}`); } /** @@ -397,7 +329,7 @@ export class Client extends EventEmitter { * @returns Key-value object of settings. */ async syncFetchSettings(keys: string[]) { - return await this.req("POST", "/sync/settings/fetch", { keys }); + return await this.api.post("/sync/settings/fetch", { keys }); } /** @@ -416,11 +348,12 @@ export class Client extends EventEmitter { typeof value === "string" ? value : JSON.stringify(value); } - const query = timestamp ? `?timestamp=${timestamp}` : ""; - await this.req( - "POST", - `/sync/settings/set${query}` as "/sync/settings/set", - requestData, + await this.api.post( + `/sync/settings/set`, + { + ...requestData, + timestamp + } ); } @@ -429,7 +362,7 @@ export class Client extends EventEmitter { * @returns Array of channel unreads. */ async syncFetchUnreads() { - return await this.req("GET", "/sync/unreads"); + return await this.api.get("/sync/unreads"); } /** @@ -442,7 +375,7 @@ export class Client extends EventEmitter { async logout(avoidRequest?: boolean) { this.user = null; this.emit("logout"); - !avoidRequest && (await this.req("POST", "/auth/session/logout")); + !avoidRequest && (await this.api.post("/auth/session/logout")); this.reset(); } @@ -467,8 +400,8 @@ export class Client extends EventEmitter { * @param data Registration data object * @returns A promise containing a registration response object */ - register(data: Route<"POST", "/auth/account/create">["data"]) { - return this.request("POST", "/auth/account/create", { data }); + register(data: DataCreateAccount) { + return this.api.post("/auth/account/create", data); } /** @@ -512,7 +445,7 @@ export class Client extends EventEmitter { * @returns Generated URL or nothing */ generateFileURL( - attachment?: { tag: string; _id: string; content_type?: string, metadata?: AttachmentMetadata }, + attachment?: { tag: string; _id: string; content_type?: string, metadata?: Metadata }, ...args: FileArgs ) { const [options, allowAnimation, fallback] = args; @@ -537,7 +470,7 @@ export class Client extends EventEmitter { query = "?" + Object.keys(options) - .map((k) => `${k}=${options[k as keyof SizeOptions]}`) + .map((k) => `${k}=${options[k as keyof FileArgs[0]]}`) .join("&"); } } diff --git a/src/api/routes.ts b/src/api/routes.ts deleted file mode 100644 index 2985e1e0..00000000 --- a/src/api/routes.ts +++ /dev/null @@ -1,853 +0,0 @@ -import { Account, Session, SessionInfo } from "revolt-api/types/Auth"; -import type { Bot, PublicBot } from "revolt-api/types/Bots"; -import type { - Channel, - DirectMessageChannel, - GroupChannel, - Message, - TextChannel, - VoiceChannel, - SendableEmbed -} from "revolt-api/types/Channels"; -import type { RevoltConfiguration } from "revolt-api/types/Core"; -import type { Invite, RetrievedInvite, ServerInvite } from "revolt-api/types/Invites"; -import type { - Ban, - Category, - Member, - Role, - Server, - SystemMessageChannels, -} from "revolt-api/types/Servers"; -import type { UserSettings, ChannelUnread } from "revolt-api/types/Sync"; -import type { - Profile, - Relationship, - RelationshipOnly, - Status, - User, -} from "revolt-api/types/Users"; -import { Override } from "revolt-api/types/_common"; - -export type RemoveUserField = - | "ProfileContent" - | "ProfileBackground" - | "StatusText" - | "Avatar"; -export type RemoveChannelField = "Icon" | "Description"; -export type RemoveServerField = "Icon" | "Banner" | "Description"; -export type RemoveMemberField = "Nickname" | "Avatar"; -export type RemoveRoleField = "Colour"; - -export type BulkMessageResponse = Message[] -| { - messages: Message[]; - users: User[]; - members?: Member[]; - }; - -type Id = "id"; -type Routes = - /** - * Core - */ - | { - // Query Revolt node. - method: "GET"; - route: `/`; - data: undefined; - response: RevoltConfiguration; - } - /** - * Auth - */ - | { - method: "GET"; - route: `/auth/account`; - data: undefined; - response: Account; - } - | { - method: "POST"; - route: `/auth/account/create`; - data: { - email: string; - password: string; - invite?: string; - captcha?: string; - }; - response: undefined; - } - | { - method: "POST"; - route: `/auth/account/reverify`; - data: { - email: string; - captcha?: string; - }; - response: undefined; - } - | { - method: "POST"; - route: `/auth/account/verify/:code`; - data: undefined; - response: undefined; - } - | { - method: "POST"; - route: `/auth/account/reset_password`; - data: { - email: string; - captcha?: string; - }; - response: undefined; - } - | { - method: "PATCH"; - route: `/auth/account/reset_password`; - data: { - password: string; - token: string; - }; - response: undefined; - } - | { - method: "PATCH"; - route: `/auth/account/change/password`; - data: { - password: string; - current_password: string; - }; - response: undefined; - } - | { - method: "PATCH"; - route: `/auth/account/change/email`; - data: { - email: string; - current_password: string; - }; - response: undefined; - } - | { - method: "POST"; - route: `/auth/session/login`; - data: { - email: string; - password?: string; - challenge?: string; - friendly_name?: string; - captcha?: string; - }; - response: Session; - } - | { - method: "POST"; - route: `/auth/session/logout`; - data: undefined; - response: undefined; - } - | { - method: "PATCH"; - route: `/auth/session/${Id}`; - data: { - friendly_name: string; - }; - response: undefined; - } - | { - method: "DELETE"; - route: `/auth/session/${Id}`; - data: undefined; - response: undefined; - } - | { - method: "GET"; - route: `/auth/session/all`; - data: undefined; - response: SessionInfo[]; - } - | { - method: "DELETE"; - route: `/auth/session/all`; - data: undefined; - response: undefined; - } - /** - * Onboarding - */ - | { - // Ask server for user status. - method: "GET"; - route: `/onboard/hello`; - data: undefined; - response: { - onboarding: boolean; - id?: string; - }; - } - | { - // Complete onboarding. - method: "POST"; - route: `/onboard/complete`; - data: { - username: string; - }; - response: { - onboarding: boolean; - }; - } - /** - * Users - */ - | { - // Retrieve a user's information. - method: "GET"; - route: `/users/${Id}`; - data: undefined; - response: User; - } - | { - // Edit user. - method: "PATCH"; - route: `/users/@me`; - data: { - status?: Status; - profile?: { - content?: string; - background?: string; - }; - avatar?: string; - remove?: RemoveUserField; - }; - response: User; - } - | { - // Change username. - method: "PATCH"; - route: `/users/@me/username`; - data: { - username: string; - password: string; - }; - response: User; - } - | { - // Retrieve a user's profile. - method: "GET"; - route: `/users/${Id}/profile`; - data: undefined; - response: Profile; - } - | { - // Fetch all your DM conversations. - method: "GET"; - route: `/users/dms`; - data: undefined; - response: (DirectMessageChannel | GroupChannel)[]; - } - | { - // Open a DM with another user. - method: "GET"; - route: `/users/${Id}/dm`; - data: undefined; - response: DirectMessageChannel; - } - | { - // Fetch all of your relationships with other users. - method: "GET"; - route: `/users/relationships`; - data: undefined; - response: Relationship[]; - } - | { - // Fetch your relationship with another other user. - method: "GET"; - route: `/users/${Id}/relationship`; - data: undefined; - response: RelationshipOnly; - } - | { - // Fetch your mutual relationships with another user. - method: "GET"; - route: `/users/${Id}/mutual`; - data: undefined; - response: { - users: string[]; - servers: string[]; - }; - } - | { - // Send a friend request / accept a friend request. - method: "PUT"; - route: `/users/${Id}/friend`; - data: undefined; - response: RelationshipOnly; - } - | { - // Delete a friend request / remove a friend. - method: "DELETE"; - route: `/users/${Id}/friend`; - data: undefined; - response: RelationshipOnly; - } - | { - // Block a user. - method: "PUT"; - route: `/users/${Id}/block`; - data: undefined; - response: RelationshipOnly; - } - | { - // Unblock a user. - method: "DELETE"; - route: `/users/${Id}/block`; - data: undefined; - response: RelationshipOnly; - } - | { - // Fetch a user's avatar. - method: "GET"; - route: `/users/${Id}/avatar`; - data: undefined; - response: unknown; - } - | { - // Fetch default avatar for user. - method: "GET"; - route: `/users/${Id}/default_avatar`; - data: undefined; - response: unknown; - } - /** - * Channels - */ - | { - // Fetch a channel by ID. - method: "GET"; - route: `/channels/${Id}`; - data: undefined; - response: Channel; - } - | { - // Fetch a channel's members by ID. - method: "GET"; - route: `/channels/${Id}/members`; - data: undefined; - response: User[]; - } - | { - // Edit a channel. - method: "PATCH"; - route: `/channels/${Id}`; - data: { - name?: string; - description?: string; - icon?: string; - nsfw?: boolean; - remove?: RemoveChannelField; - }; - response: Channel; - } - | { - // Close DM channel or leave group channel. - method: "DELETE"; - route: `/channels/${Id}`; - data: undefined; - response: undefined; - } - | { - // Create a group DM. - method: "POST"; - route: `/channels/create`; - data: { - name: string; - description?: string; - nonce: string; - users: string[]; - }; - response: GroupChannel; - } - | { - // Create an invite to a channel. - method: "POST"; - route: `/channels/${Id}/invites`; - data: undefined; - response: Invite; - } - | { - // Add member to group. - method: "PUT"; - route: `/channels/${Id}/recipients/${Id}`; - data: undefined; - response: undefined; - } - | { - // Remove member from group. - method: "DELETE"; - route: `/channels/${Id}/recipients/${Id}`; - data: undefined; - response: undefined; - } - | { - // Mark a channel as read at message. - method: "PUT"; - route: `/channels/${Id}/ack/${Id}`; - data: undefined; - response: undefined; - } - | { - // Set default permissions for channel. - method: "PUT"; - route: `/channels/${Id}/permissions/default`; - data: { - permissions?: Override | number; - }; - response: Channel; - } - | { - // Set role permission for channel. - method: "PUT"; - route: `/channels/${Id}/permissions/${Id}`; - data: { - permissions?: Override | number; - }; - response: Channel; - } - /** - * Messaging - */ - | { - // Send message to channel. - method: "POST"; - route: `/channels/${Id}/messages`; - data: { - content: string; - nonce: string; - attachments?: string[]; - replies?: { id: string; mention: boolean }[]; - embeds?: SendableEmbed[]; - }; - response: Message; - } - | { - // Fetch message from channel. - method: "GET"; - route: `/channels/${Id}/messages/${Id}`; - data: undefined; - response: Message; - } - | { - // Query messages from channel (optionally include users as well). - method: "GET"; - route: `/channels/${Id}/messages`; - data: { - limit?: number; - before?: string; - after?: string; - sort?: "Latest" | "Oldest"; - nearby?: string; - include_users?: boolean; - }; - response: BulkMessageResponse; - } - | { - // Search messages in a channel (optionally include users as well). - method: "POST"; - route: `/channels/${Id}/search`; - data: { - query: string; - - limit?: number; - before?: string; - after?: string; - sort?: "Relevance" | "Latest" | "Oldest"; - include_users?: boolean; - }; - response: BulkMessageResponse; - } - | { - // Query updated messages from channel. - method: "POST"; - route: `/channels/${Id}/messages/stale`; - data: { - ids: string[]; - }; - response: { - updated: Message[]; - deleted: string[]; - }; - } - | { - // Edit message. - method: "PATCH"; - route: `/channels/${Id}/messages/${Id}`; - data: { - content?: string; - embeds?: SendableEmbed[]; - }; - response: Message; - } - | { - // Delete message. - method: "DELETE"; - route: `/channels/${Id}/messages/${Id}`; - data: undefined; - response: undefined; - } - /** - * Servers - */ - | { - // Create a server. - method: "POST"; - route: `/servers/create`; - data: { - name: string; - nonce: string; - }; - response: Server; - } - | { - // Fetch a server by ID. - method: "GET"; - route: `/servers/${Id}`; - data: undefined; - response: Server; - } - | { - // Delete or leave a server. - method: "DELETE"; - route: `/servers/${Id}`; - data: undefined; - response: undefined; - } - | { - // Edit a server. - method: "PATCH"; - route: `/servers/${Id}`; - data: { - name?: string; - description?: string; - icon?: string; - banner?: string; - categories?: Category[]; - system_messages?: SystemMessageChannels; - nsfw?: boolean; - remove?: RemoveServerField; - }; - response: Server; - } - | { - // Mark an entire server as read. - method: "PUT"; - route: `/servers/${Id}/ack`; - data: undefined; - response: undefined; - } - | { - // Fetch a server's members - method: "GET"; - route: `/servers/${Id}/members`; - data: undefined; - response: { - members: Member[]; - users: User[]; - }; - } - | { - // Fetch a server member by their ID - method: "GET"; - route: `/servers/${Id}/members/${Id}`; - data: undefined; - response: Member; - } - | { - // Edit a server member - method: "PATCH"; - route: `/servers/${Id}/members/${Id}`; - data: { - nickname?: string; - avatar?: string; - roles?: string[]; - remove?: RemoveMemberField; - }; - response: Member; - } - | { - // Kick a server member - method: "DELETE"; - route: `/servers/${Id}/members/${Id}`; - data: undefined; - response: undefined; - } - | { - // Ban a user from the server - method: "PUT"; - route: `/servers/${Id}/bans/${Id}`; - data: { - reason?: string; - }; - response: Ban; - } - | { - // Unban a user from the server - method: "DELETE"; - route: `/servers/${Id}/bans/${Id}`; - data: undefined; - response: undefined; - } - | { - // Fetch a server's bans - method: "GET"; - route: `/servers/${Id}/bans`; - data: undefined; - response: { - users: Pick[]; - bans: Ban[]; - }; - } - | { - // Create a new server channel - method: "POST"; - route: `/servers/${Id}/channels`; - data: { - type?: "Text" | "Voice"; - name: string; - description?: string; - nonce: string; - }; - response: TextChannel | VoiceChannel; - } - | { - // Fetch a server's invites - method: "GET"; - route: `/servers/${Id}/invites`; - data: undefined; - response: ServerInvite[]; - } - | { - // Create role - method: "POST"; - route: `/servers/${Id}/roles`; - data: { - name: string; - }; - response: { - id: string; - role: Role; - }; - } - | { - // Edit a role. - method: "PATCH"; - route: `/servers/${Id}/roles/${Id}`; - data: { - name?: string; - colour?: string; - hoist?: boolean; - rank?: number; - remove?: RemoveRoleField; - }; - response: Role; - } - | { - // Delete role - method: "DELETE"; - route: `/servers/${Id}/roles/${Id}`; - data: undefined; - response: undefined; - } - | { - // Set default permission for server. - method: "PUT"; - route: `/servers/${Id}/permissions/default`; - data: { - permissions: number; - }; - response: Server; - } - | { - // Set role permission for server. - method: "PUT"; - route: `/servers/${Id}/permissions/${Id}`; - data: { - permissions: Override | number; - }; - response: Server; - } - /** - * Bots - */ - | { - // Create Bot - method: "POST"; - route: `/bots/create`; - data: { - name: string; - }; - response: Bot; - } - | { - // Fetch Owned Bots - method: "GET"; - route: `/bots/@me`; - data: undefined; - response: { - bots: Bot[]; - users: User[]; - }; - } - | { - // Fetch Bot - method: "GET"; - route: `/bots/${Id}`; - data: undefined; - response: { - bot: Bot; - user: User; - }; - } - | { - // Edit Bot - method: "PATCH"; - route: `/bots/${Id}`; - data: { - name?: string; - public?: boolean; - interactionsURL?: string; - remove?: "InteractionsURL"; - }; - response: Bot; - } - | { - // Delete Bot - method: "DELETE"; - route: `/bots/${Id}`; - data: undefined; - response: undefined; - } - | { - // Fetch Public Bot - method: "GET"; - route: `/bots/${Id}/invite`; - data: undefined; - response: PublicBot; - } - | { - // Invite Public Bot - method: "POST"; - route: `/bots/${Id}/invite`; - data: { server: string } | { group: string }; - response: undefined; - } - /** - * Invites - */ - | { - // Fetch information about an invite. - method: "GET"; - route: `/invites/${Id}`; - data: undefined; - response: RetrievedInvite; - } - | { - // Use an invite. - method: "POST"; - route: `/invites/${Id}`; - data: undefined; - response: { - type: "Server"; - channel: TextChannel; - server: Server; - }; - } - | { - // Delete an invite. - method: "DELETE"; - route: `/invites/${Id}`; - data: undefined; - response: undefined; - } - /** - * Sync - */ - | { - // Fetch user settings. - method: "POST"; - route: `/sync/settings/fetch`; - data: { - keys: string[]; - }; - response: UserSettings; - } - | { - // Set user settings. - method: "POST"; - route: `/sync/settings/set`; - data: { - [key: string]: string; - }; - response: undefined; - } - | { - // Fetch unreads. - method: "GET"; - route: `/sync/unreads`; - data: undefined; - response: ChannelUnread[]; - } - /** - * Push API - */ - | { - method: "POST"; - route: `/push/subscribe`; - data: { - endpoint: string; - p256dh: string; - auth: string; - }; - response: undefined; - } - | { - method: "POST"; - route: `/push/unsubscribe`; - data: undefined; - response: undefined; - } - /** - * Voice API - */ - | { - // Join voice call for channel. - method: "POST"; - route: `/channels/${Id}/join_call`; - data: undefined; - response: { - token: string; - }; - }; - -// ? Below are Typescript typings for extracting route data from the object above. -// ? https://artsy.github.io/blog/2018/11/21/conditional-types-in-typescript/ - -export type RoutePath = Routes["route"]; -export type RouteMethod = Routes["method"]; - -type ExcludeRouteKey = K extends "route" ? never : K; -type ExcludeRouteField = { [K in ExcludeRouteKey]: A[K] }; -type ExtractRouteParameters = A extends { route: T } - ? ExcludeRouteField - : never; - -type ExcludeMethodKey = K extends "method" ? never : K; -type ExcludeMethodField = { [K in ExcludeMethodKey]: A[K] }; -type ExtractMethodParameters = A extends { method: T } - ? ExcludeMethodField - : never; - -export type Route< - M extends RouteMethod, - T extends RoutePath, -> = ExtractMethodParameters, M>; diff --git a/src/index.ts b/src/index.ts index f9015748..afd3979d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,3 +4,4 @@ export { UserPermission, Permission } from "./api/permissions"; +export * from "revolt-api"; diff --git a/src/maps/Bots.ts b/src/maps/Bots.ts index 19e1c47a..0d925834 100644 --- a/src/maps/Bots.ts +++ b/src/maps/Bots.ts @@ -1,9 +1,9 @@ import { runInAction } from "mobx"; -import { Route } from "../api/routes"; - import { Client } from "../Client"; +import { InviteBotDestination, DataEditBot, DataCreateBot, OwnedBotsResponse } from 'revolt-api'; + export default class Bots { client: Client; @@ -17,10 +17,10 @@ export default class Bots { * @returns Bot and User object */ async fetch(id: string) { - const { bot, user } = await this.client.req( - "GET", - `/bots/${id}` as "/bots/id", + const { bot, user } = await this.client.api.get( + `/bots/${id as ''}`, ); + return { bot, user: await this.client.users.fetch(user._id, user), @@ -32,7 +32,7 @@ export default class Bots { * @param id Bot ID */ async delete(id: string) { - await this.client.req("DELETE", `/bots/${id}` as "/bots/id"); + await this.client.api.delete(`/bots/${id}`); } /** @@ -41,9 +41,8 @@ export default class Bots { * @returns Public Bot object */ async fetchPublic(id: string) { - return await this.client.req( - "GET", - `/bots/${id}/invite` as "/bots/id/invite", + return await this.client.api.get( + `/bots/${id}/invite`, ); } @@ -54,11 +53,10 @@ export default class Bots { */ async invite( id: string, - destination: Route<"POST", "/bots/id/invite">["data"], + destination: InviteBotDestination, ) { - return await this.client.req( - "POST", - `/bots/${id}/invite` as "/bots/id/invite", + return await this.client.api.post( + `/bots/${id}/invite`, destination, ); } @@ -69,10 +67,9 @@ export default class Bots { * @returns Bot and User objects */ async fetchOwned() { - const { bots, users: userObjects } = await this.client.req( - "GET", + const { bots, users: userObjects } = await this.client.api.get( `/bots/@me`, - ); + ) as OwnedBotsResponse; const users = []; for (const obj of userObjects) { @@ -87,8 +84,8 @@ export default class Bots { * @param id Bot ID * @param data Bot edit data object */ - async edit(id: string, data: Route<"PATCH", "/bots/id">["data"]) { - await this.client.req("PATCH", `/bots/${id}` as "/bots/id", data); + async edit(id: string, data: DataEditBot) { + await this.client.api.patch(`/bots/${id}`, data); if (data.name) { const user = this.client.users.get(id); @@ -104,8 +101,8 @@ export default class Bots { * Create a bot * @param data Bot creation data */ - async create(data: Route<"POST", "/bots/create">["data"]) { - const bot = await this.client.req("POST", "/bots/create", data); + async create(data: DataCreateBot) { + const bot = await this.client.api.post("/bots/create", data); const user = await this.client.users.fetch(bot._id, { _id: bot._id, username: data.name, diff --git a/src/maps/Channels.ts b/src/maps/Channels.ts index 2120d473..74a7619f 100644 --- a/src/maps/Channels.ts +++ b/src/maps/Channels.ts @@ -1,11 +1,15 @@ import type { Channel as ChannelI, + DataCreateGroup, + DataEditChannel, + DataMessageSend, + FieldsChannel, Message as MessageI, -} from "revolt-api/types/Channels"; -import type { RemoveChannelField, Route } from "../api/routes"; -import type { Attachment } from "revolt-api/types/Autumn"; -import type { Member } from "revolt-api/types/Servers"; -import type { User } from "revolt-api/types/Users"; + OptionsMessageSearch, +} from "revolt-api"; +import type { File } from "revolt-api"; +import type { Member } from "revolt-api"; +import type { User } from "revolt-api"; import { action, computed, makeAutoObservable, runInAction } from "mobx"; import isEqual from "lodash.isequal"; @@ -21,7 +25,8 @@ import { Permission, UserPermission } from "../api/permissions"; import { INotificationChecker } from "../util/Unreads"; -import { Override, OverrideField } from "revolt-api/types/_common"; +import { Override, OverrideField } from "revolt-api"; +import { APIRoutes } from "revolt-api/dist/routes"; export class Channel { client: Client; @@ -75,7 +80,7 @@ export class Channel { * Channel icon. * @requires `Group`, `TextChannel`, `VoiceChannel` */ - icon: Nullable = null; + icon: Nullable = null; /** * Channel description. @@ -279,7 +284,7 @@ export class Channel { }); } - @action update(data: Partial, clear?: RemoveChannelField) { + @action update(data: Partial, clear: FieldsChannel[] = []) { const apply = (key: string, target?: string) => { if ( // @ts-expect-error TODO: clean up types here @@ -292,13 +297,15 @@ export class Channel { } }; - switch (clear) { - case "Description": - this.description = null; - break; - case "Icon": - this.icon = null; - break; + for (const entry of clear) { + switch (entry) { + case "Description": + this.description = null; + break; + case "Icon": + this.icon = null; + break; + } } apply("active"); @@ -338,24 +345,19 @@ export class Channel { * @returns An array of the channel's members. */ async fetchMembers() { - const members = await this.client.req( - "GET", - `/channels/${this._id}/members` as "/channels/id/members", + const members = await this.client.api.get( + `/channels/${this._id as ''}/members` ); + return members.map(this.client.users.createObj); } /** * Edit a channel - * @param data Channel editing route data + * @param data Edit data */ - async edit(data: Route<"PATCH", "/channels/id">["data"]) { - return await this.client.req( - "PATCH", - `/channels/${this._id}` as "/channels/id", - data, - ); - // ! FIXME: return $set in req + async edit(data: DataEditChannel) { + this.update(await this.client.api.patch(`/channels/${this._id}`, data)); } /** @@ -364,9 +366,8 @@ export class Channel { */ async delete(avoidReq?: boolean) { if (!avoidReq) - await this.client.req( - "DELETE", - `/channels/${this._id}` as "/channels/id", + await this.client.api.delete( + `/channels/${this._id}` ); runInAction(() => { @@ -396,9 +397,8 @@ export class Channel { * @param user_id ID of the target user */ async addMember(user_id: string) { - return await this.client.req( - "PUT", - `/channels/${this._id}/recipients/${user_id}` as "/channels/id/recipients/id", + return await this.client.api.put( + `/channels/${this._id}/recipients/${user_id as ''}` ); } @@ -407,9 +407,8 @@ export class Channel { * @param user_id ID of the target user */ async removeMember(user_id: string) { - return await this.client.req( - "DELETE", - `/channels/${this._id}/recipients/${user_id}` as "/channels/id/recipients/id", + return await this.client.api.delete( + `/channels/${this._id}/recipients/${user_id as ''}` ); } @@ -421,20 +420,20 @@ export class Channel { async sendMessage( data: | string - | (Omit["data"], "nonce"> & { + | (Omit & { nonce?: string; }), ) { - const msg: Route<"POST", "/channels/id/messages">["data"] = { + const msg: DataMessageSend = { nonce: ulid(), ...(typeof data === "string" ? { content: data } : data), }; - const message = await this.client.req( - "POST", - `/channels/${this._id}/messages` as "/channels/id/messages", + const message = await this.client.api.post( + `/channels/${this._id as ''}/messages`, msg, ); + return this.client.messages.createObj(message, true); } @@ -444,10 +443,10 @@ export class Channel { * @returns The message */ async fetchMessage(message_id: string) { - const message = await this.client.req( - "GET", - `/channels/${this._id}/messages/${message_id}` as "/channels/id/messages/id", + const message = await this.client.api.get( + `/channels/${this._id as ''}/messages/${message_id as ''}`, ); + return this.client.messages.createObj(message); } @@ -458,14 +457,13 @@ export class Channel { */ async fetchMessages( params?: Omit< - Route<"GET", "/channels/id/messages">["data"], + (APIRoutes & { method: 'get', path: '/channels/{target}/messages' })['params'], "include_users" >, ) { - const messages = (await this.client.request( - "GET", - `/channels/${this._id}/messages` as "/channels/id/messages", - { params }, + const messages = (await this.client.api.get( + `/channels/${this._id}/messages`, + { ...params }, )) as MessageI[]; return runInAction(() => messages.map(this.client.messages.createObj)); } @@ -477,14 +475,13 @@ export class Channel { */ async fetchMessagesWithUsers( params?: Omit< - Route<"GET", "/channels/id/messages">["data"], + (APIRoutes & { method: 'get', path: '/channels/{target}/messages' })['params'], "include_users" >, ) { - const data = (await this.client.request( - "GET", - `/channels/${this._id}/messages` as "/channels/id/messages", - { params: { ...params, include_users: true } }, + const data = (await this.client.api.get( + `/channels/${this._id}/messages`, + { ...params, include_users: true }, )) as { messages: MessageI[]; users: User[]; members?: Member[] }; return runInAction(() => { return { @@ -502,13 +499,12 @@ export class Channel { */ async search( params: Omit< - Route<"POST", "/channels/id/search">["data"], + OptionsMessageSearch, "include_users" >, ) { - const messages = (await this.client.req( - "POST", - `/channels/${this._id}/search` as "/channels/id/search", + const messages = (await this.client.api.post( + `/channels/${this._id}/search`, params, )) as MessageI[]; return runInAction(() => messages.map(this.client.messages.createObj)); @@ -521,13 +517,12 @@ export class Channel { */ async searchWithUsers( params: Omit< - Route<"POST", "/channels/id/search">["data"], + OptionsMessageSearch, "include_users" >, ) { - const data = (await this.client.req( - "POST", - `/channels/${this._id}/search` as "/channels/id/search", + const data = (await this.client.api.post( + `/channels/${this._id}/search`, { ...params, include_users: true }, )) as { messages: MessageI[]; users: User[]; members?: Member[] }; return runInAction(() => { @@ -545,9 +540,8 @@ export class Channel { * @returns The stale messages */ async fetchStale(ids: string[]) { - const data = await this.client.req( - "POST", - `/channels/${this._id}/messages/stale` as "/channels/id/messages/stale", + /*const data = await this.client.api.post( + `/channels/${this._id}/messages/stale`, { ids }, ); @@ -558,7 +552,8 @@ export class Channel { ); }); - return data; + return data;*/ + return { deprecated: ids }; } /** @@ -566,9 +561,8 @@ export class Channel { * @returns Newly created invite code */ async createInvite() { - return await this.client.req( - "POST", - `/channels/${this._id}/invites` as "/channels/id/invites", + return await this.client.api.post( + `/channels/${this._id}/invites`, ); } @@ -577,9 +571,8 @@ export class Channel { * @returns Join call response data */ async joinCall() { - return await this.client.req( - "POST", - `/channels/${this._id}/join_call` as "/channels/id/join_call", + return await this.client.api.post( + `/channels/${this._id as ''}/join_call`, ); } @@ -598,9 +591,8 @@ export class Channel { ulid(); const performAck = () => { delete this.ackLimit; - this.client.req( - "PUT", - `/channels/${this._id}/ack/${id}` as "/channels/id/ack/id", + this.client.api.put( + `/channels/${this._id}/ack/${id as ''}`, ); }; @@ -626,9 +618,8 @@ export class Channel { * @param permissions Permission value */ async setPermissions(role_id = "default", permissions: Override) { - return await this.client.req( - "PUT", - `/channels/${this._id}/permissions/${role_id}` as "/channels/id/permissions/id", + return await this.client.api.put( + `/channels/${this._id}/permissions/${role_id as ''}`, { permissions }, ); } @@ -764,7 +755,7 @@ export default class Channels extends Collection { if (this.has(id)) return this.$get(id); const res = data ?? - (await this.client.req("GET", `/channels/${id}` as "/channels/id")); + (await this.client.api.get(`/channels/${id as ''}`)); return this.createObj(res); } @@ -792,8 +783,8 @@ export default class Channels extends Collection { * @param data Group create route data * @returns The newly-created group */ - async createGroup(data: Route<"POST", "/channels/create">["data"]) { - const group = await this.client.req("POST", `/channels/create`, data); + async createGroup(data: DataCreateGroup) { + const group = await this.client.api.post(`/channels/create`, data); return (await this.fetch(group._id, group))!; } } diff --git a/src/maps/Members.ts b/src/maps/Members.ts index d16a854b..f65e226e 100644 --- a/src/maps/Members.ts +++ b/src/maps/Members.ts @@ -1,10 +1,11 @@ import type { + DataMemberEdit, + FieldsMember, Member as MemberI, MemberCompositeKey, Role, -} from "revolt-api/types/Servers"; -import type { RemoveMemberField, Route } from "../api/routes"; -import type { Attachment } from "revolt-api/types/Autumn"; +} from "revolt-api"; +import type { File } from "revolt-api"; import { makeAutoObservable, runInAction, action, computed } from "mobx"; import isEqual from "lodash.isequal"; @@ -19,7 +20,7 @@ export class Member { _id: MemberCompositeKey; nickname: Nullable = null; - avatar: Nullable = null; + avatar: Nullable = null; roles: Nullable = null; /** @@ -50,7 +51,7 @@ export class Member { }); } - @action update(data: Partial, clear?: RemoveMemberField) { + @action update(data: Partial, clear: FieldsMember[] = []) { const apply = (key: string) => { // This code has been tested. if ( @@ -64,13 +65,15 @@ export class Member { } }; - switch (clear) { - case "Nickname": - this.nickname = null; - break; - case "Avatar": - this.avatar = null; - break; + for (const field of clear) { + switch (field) { + case "Nickname": + this.nickname = null; + break; + case "Avatar": + this.avatar = null; + break; + } } apply("nickname"); @@ -83,10 +86,9 @@ export class Member { * @param data Member editing route data * @returns Server member object */ - async edit(data: Route<"PATCH", "/servers/id/members/id">["data"]) { - return await this.client.req( - "PATCH", - `/servers/${this._id.server}/members/${this._id.user}` as "/servers/id/members/id", + async edit(data: DataMemberEdit) { + return await this.client.api.patch( + `/servers/${this._id.server as ''}/members/${this._id.user}`, data, ); } @@ -96,9 +98,8 @@ export class Member { * @param user_id User ID */ async kick() { - return await this.client.req( - "DELETE", - `/servers/${this._id.server}/members/${this._id.user}` as "/servers/id/members/id", + return await this.client.api.delete( + `/servers/${this._id.server}/members/${this._id.user as ''}`, ); } diff --git a/src/maps/Messages.ts b/src/maps/Messages.ts index dec0f44a..2f48b164 100644 --- a/src/maps/Messages.ts +++ b/src/maps/Messages.ts @@ -1,11 +1,12 @@ import type { + DataEditMessage, + DataMessageSend, Embed, Masquerade, Message as MessageI, SystemMessage, -} from "revolt-api/types/Channels"; -import type { Attachment } from "revolt-api/types/Autumn"; -import type { Route } from "../api/routes"; +} from "revolt-api"; +import type { File } from "revolt-api"; import { makeAutoObservable, runInAction, action, computed } from "mobx"; import isEqual from "lodash.isequal"; @@ -24,7 +25,7 @@ export class Message { author_id: string; content: string | SystemMessage; - attachments: Nullable; + attachments: Nullable; edited: Nullable; embeds: Nullable; mention_ids: Nullable; @@ -113,13 +114,13 @@ export class Message { constructor(client: Client, data: MessageI) { this.client = client; this._id = data._id; - this.nonce = data.nonce; + this.nonce = data.nonce ?? undefined; this.channel_id = data.channel; this.author_id = data.author; this.content = data.content; this.attachments = toNullable(data.attachments); - this.edited = toNullableDate(data.edited); + this.edited = toNullableDate(data.edited as any); this.embeds = toNullable(data.embeds); this.mention_ids = toNullable(data.mentions); this.reply_ids = toNullable(data.replies); @@ -164,10 +165,9 @@ export class Message { * Edit a message * @param data Message edit route data */ - async edit(data: Route<"PATCH", "/channels/id/messages/id">["data"]) { - return await this.client.req( - "PATCH", - `/channels/${this.channel_id}/messages/${this._id}` as "/channels/id/messages/id", + async edit(data: DataEditMessage) { + return await this.client.api.patch( + `/channels/${this.channel_id}/messages/${this._id as ''}`, data, ); } @@ -176,9 +176,8 @@ export class Message { * Delete a message */ async delete() { - return await this.client.req( - "DELETE", - `/channels/${this.channel_id}/messages/${this._id}` as "/channels/id/messages/id", + return await this.client.api.delete( + `/channels/${this.channel_id}/messages/${this._id as ''}`, ); } @@ -195,7 +194,7 @@ export class Message { reply( data: | string - | (Omit["data"], "nonce"> & { + | (Omit & { nonce?: string; }), mention = true, diff --git a/src/maps/Servers.ts b/src/maps/Servers.ts index 6d20f15a..8a8fbfb8 100644 --- a/src/maps/Servers.ts +++ b/src/maps/Servers.ts @@ -1,12 +1,17 @@ import type { Category, + DataBanCreate, + DataCreateChannel, + DataCreateServer, + DataEditRole, + DataEditServer, + FieldsServer, Member, Role, Server as ServerI, SystemMessageChannels, -} from "revolt-api/types/Servers"; -import type { RemoveServerField, Route } from "../api/routes"; -import type { Attachment } from "revolt-api/types/Autumn"; +} from "revolt-api"; +import type { File } from "revolt-api"; import { makeAutoObservable, action, runInAction, computed } from "mobx"; import isEqual from "lodash.isequal"; @@ -18,7 +23,7 @@ import { User } from "./Users"; import { Client, FileArgs } from ".."; import { decodeTime } from "ulid"; import { INotificationChecker } from "../util/Unreads"; -import { Override } from "revolt-api/types/_common"; +import { Override } from "revolt-api"; import Long from "long"; export class Server { @@ -36,8 +41,8 @@ export class Server { roles: Nullable<{ [key: string]: Role }> = null; default_permissions: number; - icon: Nullable = null; - banner: Nullable = null; + icon: Nullable = null; + banner: Nullable = null; nsfw: Nullable = null; flags: Nullable = null; @@ -126,7 +131,7 @@ export class Server { }); } - @action update(data: Partial, clear?: RemoveServerField) { + @action update(data: Partial, clear: FieldsServer[] = []) { const apply = (key: string, target?: string) => { // This code has been tested. if ( @@ -140,16 +145,18 @@ export class Server { } }; - switch (clear) { - case "Banner": - this.banner = null; - break; - case "Description": - this.description = null; - break; - case "Icon": - this.icon = null; - break; + for (const entry of clear) { + switch (entry) { + case "Banner": + this.banner = null; + break; + case "Description": + this.description = null; + break; + case "Icon": + this.icon = null; + break; + } } apply("owner"); @@ -171,10 +178,9 @@ export class Server { * @param data Channel create route data * @returns The newly-created channel */ - async createChannel(data: Route<"POST", "/servers/id/channels">["data"]) { - return await this.client.req( - "POST", - `/servers/${this._id}/channels` as "/servers/id/channels", + async createChannel(data: DataCreateChannel) { + return await this.client.api.post( + `/servers/${this._id}/channels`, data, ); } @@ -183,10 +189,9 @@ export class Server { * Edit a server * @param data Server editing route data */ - async edit(data: Route<"PATCH", "/servers/id">["data"]) { - return await this.client.req( - "PATCH", - `/servers/${this._id}` as "/servers/id", + async edit(data: DataEditServer) { + return await this.client.api.patch( + `/servers/${this._id}`, data, ); } @@ -196,9 +201,8 @@ export class Server { */ async delete(avoidReq?: boolean) { if (!avoidReq) - await this.client.req( - "DELETE", - `/servers/${this._id}` as "/servers/id", + await this.client.api.delete( + `/servers/${this._id}`, ); runInAction(() => { @@ -210,9 +214,8 @@ export class Server { * Mark a server as read */ async ack() { - return await this.client.req( - "PUT", - `/servers/${this._id}/ack` as "/servers/id/ack", + return await this.client.api.put( + `/servers/${this._id}/ack`, ); } @@ -222,11 +225,10 @@ export class Server { */ async banUser( user_id: string, - data: Route<"PUT", "/servers/id/bans/id">["data"], + data: DataBanCreate, ) { - return await this.client.req( - "PUT", - `/servers/${this._id}/bans/${user_id}` as "/servers/id/bans/id", + return await this.client.api.put( + `/servers/${this._id as ''}/bans/${user_id}`, data, ); } @@ -236,9 +238,8 @@ export class Server { * @param user_id User ID */ async unbanUser(user_id: string) { - return await this.client.req( - "DELETE", - `/servers/${this._id}/bans/${user_id}` as "/servers/id/bans/id", + return await this.client.api.delete( + `/servers/${this._id as ''}/bans/${user_id}`, ); } @@ -247,9 +248,8 @@ export class Server { * @returns An array of the server's invites */ async fetchInvites() { - return await this.client.req( - "GET", - `/servers/${this._id}/invites` as "/servers/id/invites", + return await this.client.api.get( + `/servers/${this._id}/invites`, ); } @@ -258,9 +258,8 @@ export class Server { * @returns An array of the server's bans. */ async fetchBans() { - return await this.client.req( - "GET", - `/servers/${this._id}/bans` as "/servers/id/bans", + return await this.client.api.get( + `/servers/${this._id}/bans` ); } @@ -273,10 +272,9 @@ export class Server { role_id = "default", permissions: Override | number ) { - return await this.client.req( - "PUT", - `/servers/${this._id}/permissions/${role_id}` as "/servers/id/permissions/id", - { permissions }, + return await this.client.api.put( + `/servers/${this._id}/permissions/${role_id as ''}`, + { permissions: permissions as Override }, ); } @@ -285,9 +283,8 @@ export class Server { * @param name Role name */ async createRole(name: string) { - return await this.client.req( - "POST", - `/servers/${this._id}/roles` as "/servers/id/roles", + return await this.client.api.post( + `/servers/${this._id}/roles`, { name }, ); } @@ -299,11 +296,10 @@ export class Server { */ async editRole( role_id: string, - data: Route<"PATCH", "/servers/id/roles/id">["data"], + data: DataEditRole, ) { - return await this.client.req( - "PATCH", - `/servers/${this._id}/roles/${role_id}` as "/servers/id/roles/id", + return await this.client.api.patch( + `/servers/${this._id}/roles/${role_id as ''}`, data, ); } @@ -313,9 +309,8 @@ export class Server { * @param role_id Role ID */ async deleteRole(role_id: string) { - return await this.client.req( - "DELETE", - `/servers/${this._id}/roles/${role_id}` as "/servers/id/roles/id", + return await this.client.api.delete( + `/servers/${this._id}/roles/${role_id as ''}`, ); } @@ -332,9 +327,8 @@ export class Server { }); if (existing) return existing; - const member = await this.client.req( - "GET", - `/servers/${this._id}/members/${user_id}` as "/servers/id/members/id", + const member = await this.client.api.get( + `/servers/${this._id as ''}/members/${user_id as ''}`, ); return this.client.members.createObj(member); } @@ -344,16 +338,10 @@ export class Server { * ! OPTIMISATION */ async syncMembers(skipOffline?: boolean) { - const data = await this.client.req( - "GET", - `/servers/${this._id}/members` as "/servers/id/members", + const data = await this.client.api.get( + `/servers/${this._id as ''}/members`, ); - // ! FIXME: if we do this on the server, we can save 7ms locally - // 7ms to sort both lists. - // data.users.sort((a,b)=>b._id.localeCompare(a._id)); - // data.members.sort((a,b)=>b._id.user.localeCompare(a._id.user)); - // This takes roughly 23ms. runInAction(() => { // This adds roughly 15ms to 23ms above. @@ -396,9 +384,8 @@ export class Server { * @returns An array of the server's members and their user objects. */ async fetchMembers() { - const data = await this.client.req( - "GET", - `/servers/${this._id}/members` as "/servers/id/members", + const data = await this.client.api.get( + `/servers/${this._id as ''}/members`, ); // Note: this takes 986 ms (Testers server) @@ -483,7 +470,7 @@ export default class Servers extends Collection { if (this.has(id)) return this.$get(id, data); const res = data ?? - (await this.client.req("GET", `/servers/${id}` as "/servers/id")); + (await this.client.api.get(`/servers/${id as ''}`)); return runInAction(async () => { for (const channel of res.channels) { @@ -521,8 +508,8 @@ export default class Servers extends Collection { * @param data Server create route data * @returns The newly-created server */ - async createServer(data: Route<"POST", "/servers/create">["data"]) { - const server = await this.client.req("POST", `/servers/create`, data); + async createServer(data: DataCreateServer) { + const server = await this.client.api.post(`/servers/create`, data); return this.fetch(server._id, server); } } diff --git a/src/maps/Users.ts b/src/maps/Users.ts index b836ad4c..158d9dfa 100644 --- a/src/maps/Users.ts +++ b/src/maps/Users.ts @@ -1,10 +1,12 @@ import type { BotInformation, - Status, + UserStatus, User as UserI, -} from "revolt-api/types/Users"; -import type { RemoveUserField, Route } from "../api/routes"; -import type { Attachment } from "revolt-api/types/Autumn"; + RelationshipStatus, + FieldsUser, + DataEditUser, +} from "revolt-api"; +import type { File } from "revolt-api"; import { makeAutoObservable, action, runInAction, computed } from "mobx"; import isEqual from "lodash.isequal"; @@ -16,25 +18,15 @@ import { Client, FileArgs } from ".."; import _ from "lodash"; import { decodeTime } from "ulid"; -enum RelationshipStatus { - None = "None", - User = "User", - Friend = "Friend", - Outgoing = "Outgoing", - Incoming = "Incoming", - Blocked = "Blocked", - BlockedOther = "BlockedOther", -} - export class User { client: Client; _id: string; username: string; - avatar: Nullable; + avatar: Nullable; badges: Nullable; - status: Nullable; + status: Nullable; relationship: Nullable; online: Nullable; flags: Nullable; @@ -67,7 +59,7 @@ export class User { }); } - @action update(data: Partial, clear?: RemoveUserField) { + @action update(data: Partial, clear: FieldsUser[] = []) { const apply = (key: string) => { // This code has been tested. if ( @@ -85,13 +77,15 @@ export class User { } }; - switch (clear) { - case "Avatar": - this.avatar = null; - break; - case "StatusText": { - if (this.status) { - this.status.text = undefined; + for (const entry of clear) { + switch (entry) { + case "Avatar": + this.avatar = null; + break; + case "StatusText": { + if (this.status) { + this.status.text = undefined; + } } } } @@ -114,11 +108,7 @@ export class User { let dm = [...this.client.channels.values()].find(x => x.channel_type === 'DirectMessage' && x.recipient == this); if (!dm) { - const data = await this.client.req( - "GET", - `/users/${this._id}/dm` as "/users/id/dm", - ); - + const data = await this.client.api.get(`/users/${this._id as ''}/dm`); dm = await this.client.channels.fetch(data._id, data)!; } @@ -133,40 +123,28 @@ export class User { * Send a friend request to a user */ async addFriend() { - await this.client.req( - "PUT", - `/users/${this.username}/friend` as "/users/id/friend", - ); + await this.client.api.put(`/users/${this.username as ''}/friend`); } /** * Remove a user from the friend list */ async removeFriend() { - await this.client.req( - "DELETE", - `/users/${this._id}/friend` as "/users/id/friend", - ); + await this.client.api.delete(`/users/${this._id}/friend`); } /** * Block a user */ async blockUser() { - await this.client.req( - "PUT", - `/users/${this._id}/block` as "/users/id/block", - ); + await this.client.api.put(`/users/${this._id}/block`); } /** * Unblock a user */ async unblockUser() { - await this.client.req( - "DELETE", - `/users/${this._id}/block` as "/users/id/block", - ); + await this.client.api.delete(`/users/${this._id}/block`); } /** @@ -174,10 +152,7 @@ export class User { * @returns The profile of the user */ async fetchProfile() { - return await this.client.req( - "GET", - `/users/${this._id}/profile` as "/users/id/profile", - ); + return await this.client.api.get(`/users/${this._id}/profile`); } /** @@ -185,10 +160,7 @@ export class User { * @returns The mutual connections of the current user and a target user */ async fetchMutual() { - return await this.client.req( - "GET", - `/users/${this._id}/mutual` as "/users/id/mutual", - ); + return await this.client.api.get(`/users/${this._id}/mutual`); } /** @@ -208,14 +180,14 @@ export class User { @computed get permission() { let permissions = 0; switch (this.relationship) { - case RelationshipStatus.Friend: - case RelationshipStatus.User: + case 'Friend': + case 'User': return U32_MAX; - case RelationshipStatus.Blocked: - case RelationshipStatus.BlockedOther: + case 'Blocked': + case 'BlockedOther': return UserPermission.Access; - case RelationshipStatus.Incoming: - case RelationshipStatus.Outgoing: + case 'Incoming': + case 'Outgoing': permissions = UserPermission.Access; } @@ -265,7 +237,8 @@ export default class Users extends Collection { if (this.has(id)) return this.$get(id, data); const res = data ?? - (await this.client.req("GET", `/users/${id}` as "/users/id")); + (await this.client.api.get(`/users/${id as ''}`)); + return this.createObj(res); } @@ -291,8 +264,8 @@ export default class Users extends Collection { * Edit the current user * @param data User edit data object */ - async edit(data: Route<"PATCH", "/users/@me">["data"]) { - await this.client.req("PATCH", "/users/@me", data); + async edit(data: DataEditUser) { + await this.client.api.patch("/users/@me", data); } /** @@ -301,7 +274,7 @@ export default class Users extends Collection { * @param password Current password */ async changeUsername(username: string, password: string) { - return await this.client.req("PATCH", "/users/@me/username", { + return await this.client.api.patch("/users/@me/username", { username, password, }); diff --git a/src/tester.ts b/src/tester.ts index 11a3c4b7..8281ec23 100644 --- a/src/tester.ts +++ b/src/tester.ts @@ -18,7 +18,6 @@ async function user() { const group = await client.channels.createGroup({ name: 'sussy', - nonce: ''+Math.random(), users: [] }); @@ -26,14 +25,13 @@ async function user() { content: "embed test", embeds: [ { - type: 'Text', title: 'We do a little!' } ] }); await msg.edit({ - embeds: [{ type: 'Text', title: 'sus' }] + embeds: [{ title: 'sus' }] }); }); @@ -41,38 +39,32 @@ async function user() { if (message.content === "sus") { message.channel!.sendMessage("sus!"); } else if (message.content === "bot") { - const bot = await client.req("POST", "/bots/create", { + const bot = await client.api.post("/bots/create", { name: "basedbot12", }); message.channel!.sendMessage(JSON.stringify(bot)); } else if (message.content === "my bots") { message.channel!.sendMessage( - JSON.stringify(await client.req("GET", "/bots/@me")), + JSON.stringify(await client.api.get("/bots/@me")), ); } else if (message.content === "join bot") { - await client.req( - "POST", - `/bots/01FCV7DCMRD9MT3JBYT5VEKVRD/invite` as "/bots/id/invite", + await client.api.post( + `/bots/${'01FCV7DCMRD9MT3JBYT5VEKVRD'}/invite`, { group: message.channel_id }, ); // { server: '01FATEGMHEE2M1QGPA65NS6V8K' }); } else if (message.content === "edit bot name") { - await client.req( - "PATCH", - `/bots/01FCV7DCMRD9MT3JBYT5VEKVRD` as "/bots/id", + await client.api.patch( + `/bots/${'01FCV7DCMRD9MT3JBYT5VEKVRD'}`, { name: "testingbkaka" }, ); } else if (message.content === "make bot public") { - await client.req( - "PATCH", - `/bots/01FCV7DCMRD9MT3JBYT5VEKVRD` as "/bots/id", + await client.api.patch( + `/bots/${'01FCV7DCMRD9MT3JBYT5VEKVRD'}`, { public: true }, ); } else if (message.content === "delete bot") { - await client.req( - "DELETE", - `/bots/01FCV7DCMRD9MT3JBYT5VEKVRD` as "/bots/id", - ); + await client.api.delete(`/bots/${'01FCV7DCMRD9MT3JBYT5VEKVRD'}`); } }); diff --git a/src/util/Unreads.ts b/src/util/Unreads.ts index a6eda309..fcc800ae 100644 --- a/src/util/Unreads.ts +++ b/src/util/Unreads.ts @@ -6,7 +6,7 @@ import { ObservableMap, runInAction, } from "mobx"; -import type { ChannelUnread } from "revolt-api/types/Sync"; +import type { ChannelUnread } from "revolt-api"; import { ulid } from "ulid"; import { Channel } from "../maps/Channels"; import { Server } from "../maps/Servers"; diff --git a/src/websocket/client.ts b/src/websocket/client.ts index f6e28e1d..847ce427 100644 --- a/src/websocket/client.ts +++ b/src/websocket/client.ts @@ -1,7 +1,7 @@ import { backOff } from "@insertish/exponential-backoff"; import WebSocket from "@insertish/isomorphic-ws"; import { runInAction } from "mobx"; -import { Role } from "revolt-api/types/Servers"; +import { Role } from "revolt-api"; import { Client } from ".."; import { diff --git a/src/websocket/notifications.ts b/src/websocket/notifications.ts index 55a63f69..1ecdf124 100644 --- a/src/websocket/notifications.ts +++ b/src/websocket/notifications.ts @@ -1,20 +1,12 @@ -import type { Session } from "revolt-api/types/Auth"; -import type { Channel, Message } from "revolt-api/types/Channels"; +import type { FieldsChannel, FieldsMember, FieldsServer, FieldsUser, Session } from "revolt-api"; +import type { Channel, Message } from "revolt-api"; import type { Member, MemberCompositeKey, Role, Server, -} from "revolt-api/types/Servers"; -import type { UserSettings } from "revolt-api/types/Sync"; -import type { RelationshipStatus, User } from "revolt-api/types/Users"; - -import type { - RemoveChannelField, - RemoveServerField, - RemoveUserField, - RemoveMemberField, -} from "../api/routes"; +} from "revolt-api"; +import type { RelationshipStatus, User } from "revolt-api"; type WebSocketError = { error: @@ -60,7 +52,7 @@ export type ClientboundNotification = type: "ChannelUpdate"; id: string; data: Partial; - clear?: RemoveChannelField; + clear?: FieldsChannel[]; } | { type: "ChannelDelete"; id: string } | { type: "ChannelGroupJoin"; id: string; user: string } @@ -72,14 +64,14 @@ export type ClientboundNotification = type: "ServerUpdate"; id: string; data: Partial; - clear?: RemoveServerField; + clear?: FieldsServer[]; } | { type: "ServerDelete"; id: string } | { type: "ServerMemberUpdate"; id: MemberCompositeKey; data: Partial; - clear?: RemoveMemberField; + clear?: FieldsMember[]; } | { type: "ServerMemberJoin"; id: string; user: string } | { type: "ServerMemberLeave"; id: string; user: string } @@ -94,8 +86,8 @@ export type ClientboundNotification = type: "UserUpdate"; id: string; data: Partial; - clear?: RemoveUserField; + clear?: FieldsUser[]; } | { type: "UserRelationship"; user: User; status: RelationshipStatus } | { type: "UserPresence"; id: string; online: boolean } - | { type: "UserSettingsUpdate"; id: string; update: UserSettings }; + | { type: "UserSettingsUpdate"; id: string; update: { [key: string]: [number, string] } }; diff --git a/yarn.lock b/yarn.lock index c8ccbbac..11762dad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -41,6 +41,16 @@ resolved "https://registry.yarnpkg.com/@insertish/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#5bcd6f73b93efa9ccdb6abf887ae808d40827169" integrity sha512-kFD/p8T4Hkqr992QrdkbW/cQ/W/q2d9MPCobwzBv2PwTKLkCD9RaYDy6m17qRnSLQQ5PU0kHCG8kaOwAqzj1vQ== +"@insertish/oapi@0.1.13": + version "0.1.13" + resolved "https://registry.yarnpkg.com/@insertish/oapi/-/oapi-0.1.13.tgz#92579172bd6896152e9b68ef5bafcf8cc2048492" + integrity sha512-yj1Jk3VlNCjp0hNOsknevyR7PwVzSzc5pbUTuyhtyPlbuHRTv1b9DWs8Ki7xbF/mrIy2DzkHoUu4Ik/Q4oS93w== + dependencies: + typescript "^4.6.2" + optionalDependencies: + axios "^0.26.1" + openapi-typescript "^5.2.0" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -234,6 +244,13 @@ axios@^0.21.4: dependencies: follow-redirects "^1.14.0" +axios@^0.26.1: + version "0.26.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" + integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== + dependencies: + follow-redirects "^1.14.8" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -539,6 +556,11 @@ follow-redirects@^1.14.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== +follow-redirects@^1.14.8: + version "1.14.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" + integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== + from@~0: version "0.1.7" resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" @@ -599,6 +621,11 @@ globals@^13.6.0, globals@^13.9.0: dependencies: type-fest "^0.20.2" +globalyzer@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" + integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== + globby@^11.0.4: version "11.0.4" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" @@ -611,6 +638,11 @@ globby@^11.0.4: merge2 "^1.3.0" slash "^3.0.0" +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== + handlebars@^4.7.7: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" @@ -815,6 +847,11 @@ micromatch@^4.0.4: braces "^3.0.1" picomatch "^2.2.3" +mime@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + minimatch@^3.0.0, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -871,6 +908,18 @@ onigasm@^2.2.5: dependencies: lru-cache "^5.1.1" +openapi-typescript@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/openapi-typescript/-/openapi-typescript-5.2.0.tgz#c0712d7a17e4502ac083162c42f946c0e966a505" + integrity sha512-EGoPTmxrpiN40R6An5Wqol2l74sRb773pv/KXWYCQaMCXNtMGN7Jv+y/jY4B1Bd4hsIW2j9GFmQXxqfGmOXaxA== + dependencies: + js-yaml "^4.1.0" + mime "^3.0.0" + prettier "^2.5.1" + tiny-glob "^0.2.9" + undici "^4.14.1" + yargs-parser "^21.0.0" + optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -927,6 +976,11 @@ prettier@^2.4.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== +prettier@^2.5.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.0.tgz#12f8f504c4d8ddb76475f441337542fa799207d4" + integrity sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A== + progress@^2.0.0, progress@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -964,10 +1018,14 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -revolt-api@0.5.3-alpha.12: - version "0.5.3-alpha.12" - resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.3-alpha.12.tgz#78f25b567b840c1fd072595526592a422cb01f25" - integrity sha512-MM42oI5+5JJMnAs3JiOwSQOy/SUYzYs3M8YRC5QI4G6HU7CfyB2HNWh5jFsyRlcLdSi13dGazHm31FUPHsxOzw== +revolt-api@0.5.3-rc.7: + version "0.5.3-rc.7" + resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.3-rc.7.tgz#eed82fcd014a25a4d080c4502c0ae4aa283adddb" + integrity sha512-72SJ+P19nRRD+xE8bXAV8mFZNBgtKkXogQACDvNSIdATUmszYl91YRPqX+UQ1Oz4ePgKFwyWPxrnmfede7Q83g== + dependencies: + "@insertish/oapi" "0.1.13" + axios "^0.26.1" + lodash.defaultsdeep "^4.6.1" rimraf@^3.0.2: version "3.0.2" @@ -1076,6 +1134,14 @@ through@2, through@~2.3, through@~2.3.1: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +tiny-glob@^0.2.9: + version "0.2.9" + resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" + integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== + dependencies: + globalyzer "0.1.0" + globrex "^0.1.2" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -1142,6 +1208,11 @@ typescript@^4.4.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== +typescript@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" + integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== + uglify-js@^3.1.4: version "3.13.10" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.10.tgz#a6bd0d28d38f592c3adb6b180ea6e07e1e540a8d" @@ -1152,6 +1223,11 @@ ulid@^2.3.0: resolved "https://registry.yarnpkg.com/ulid/-/ulid-2.3.0.tgz#93063522771a9774121a84d126ecd3eb9804071f" integrity sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw== +undici@^4.14.1: + version "4.16.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-4.16.0.tgz#469bb87b3b918818d3d7843d91a1d08da357d5ff" + integrity sha512-tkZSECUYi+/T1i4u+4+lwZmQgLXd4BLGlrc7KZPcLIW7Jpq99+Xpc30ONv7nS6F5UNOxp/HBZSSL9MafUrvJbw== + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -1205,3 +1281,8 @@ yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@^21.0.0: + version "21.0.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" + integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==