refactor: Document relevant types as @internal (#9974)

* refactor: mark data resolver as internal

* docs: mark relevant TypeScript symbols as `@internal`

* docs: extra additions

* style: prefer at end

* docs: add more `@internal`s

* test: update template code
This commit is contained in:
Jiralite
2023-12-05 18:15:08 +00:00
committed by GitHub
parent 62e31cb9ee
commit 2b8ac35e56
19 changed files with 215 additions and 177 deletions

View File

@@ -23,7 +23,7 @@ const StickerPack = require('../structures/StickerPack');
const VoiceRegion = require('../structures/VoiceRegion'); const VoiceRegion = require('../structures/VoiceRegion');
const Webhook = require('../structures/Webhook'); const Webhook = require('../structures/Webhook');
const Widget = require('../structures/Widget'); const Widget = require('../structures/Widget');
const DataResolver = require('../util/DataResolver'); const { resolveInviteCode, resolveGuildTemplateCode } = require('../util/DataResolver');
const Events = require('../util/Events'); const Events = require('../util/Events');
const IntentsBitField = require('../util/IntentsBitField'); const IntentsBitField = require('../util/IntentsBitField');
const Options = require('../util/Options'); const Options = require('../util/Options');
@@ -273,7 +273,7 @@ class Client extends BaseClient {
* .catch(console.error); * .catch(console.error);
*/ */
async fetchInvite(invite, options) { async fetchInvite(invite, options) {
const code = DataResolver.resolveInviteCode(invite); const code = resolveInviteCode(invite);
const query = makeURLSearchParams({ const query = makeURLSearchParams({
with_counts: true, with_counts: true,
with_expiration: true, with_expiration: true,
@@ -293,7 +293,7 @@ class Client extends BaseClient {
* .catch(console.error); * .catch(console.error);
*/ */
async fetchGuildTemplate(template) { async fetchGuildTemplate(template) {
const code = DataResolver.resolveGuildTemplateCode(template); const code = resolveGuildTemplateCode(template);
const data = await this.rest.get(Routes.template(code)); const data = await this.rest.get(Routes.template(code));
return new GuildTemplate(this, data); return new GuildTemplate(this, data);
} }

View File

@@ -26,7 +26,7 @@ exports.ChannelFlagsBitField = require('./util/ChannelFlagsBitField');
exports.Collection = require('@discordjs/collection').Collection; exports.Collection = require('@discordjs/collection').Collection;
exports.Constants = require('./util/Constants'); exports.Constants = require('./util/Constants');
exports.Colors = require('./util/Colors'); exports.Colors = require('./util/Colors');
exports.DataResolver = require('./util/DataResolver'); __exportStar(require('./util/DataResolver.js'), exports);
exports.Events = require('./util/Events'); exports.Events = require('./util/Events');
exports.Formatters = require('./util/Formatters'); exports.Formatters = require('./util/Formatters');
exports.GuildMemberFlagsBitField = require('./util/GuildMemberFlagsBitField').GuildMemberFlagsBitField; exports.GuildMemberFlagsBitField = require('./util/GuildMemberFlagsBitField').GuildMemberFlagsBitField;

View File

@@ -13,7 +13,7 @@ const Webhook = require('../structures/Webhook');
const ChannelFlagsBitField = require('../util/ChannelFlagsBitField'); const ChannelFlagsBitField = require('../util/ChannelFlagsBitField');
const { transformGuildForumTag, transformGuildDefaultReaction } = require('../util/Channels'); const { transformGuildForumTag, transformGuildDefaultReaction } = require('../util/Channels');
const { ThreadChannelTypes } = require('../util/Constants'); const { ThreadChannelTypes } = require('../util/Constants');
const DataResolver = require('../util/DataResolver'); const { resolveImage } = require('../util/DataResolver');
const { setPosition } = require('../util/Util'); const { setPosition } = require('../util/Util');
let cacheWarningEmitted = false; let cacheWarningEmitted = false;
@@ -219,7 +219,7 @@ class GuildChannelManager extends CachedManager {
const id = this.resolveId(channel); const id = this.resolveId(channel);
if (!id) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable'); if (!id) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable');
if (typeof avatar === 'string' && !avatar.startsWith('data:')) { if (typeof avatar === 'string' && !avatar.startsWith('data:')) {
avatar = await DataResolver.resolveImage(avatar); avatar = await resolveImage(avatar);
} }
const data = await this.client.rest.post(Routes.channelWebhooks(id), { const data = await this.client.rest.post(Routes.channelWebhooks(id), {
body: { body: {

View File

@@ -4,7 +4,7 @@ const { Collection } = require('@discordjs/collection');
const { Routes, PermissionFlagsBits } = require('discord-api-types/v10'); const { Routes, PermissionFlagsBits } = require('discord-api-types/v10');
const BaseGuildEmojiManager = require('./BaseGuildEmojiManager'); const BaseGuildEmojiManager = require('./BaseGuildEmojiManager');
const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors'); const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors');
const DataResolver = require('../util/DataResolver'); const { resolveImage } = require('../util/DataResolver');
/** /**
* Manages API methods for GuildEmojis and stores their cache. * Manages API methods for GuildEmojis and stores their cache.
@@ -50,7 +50,7 @@ class GuildEmojiManager extends BaseGuildEmojiManager {
* .catch(console.error); * .catch(console.error);
*/ */
async create({ attachment, name, roles, reason }) { async create({ attachment, name, roles, reason }) {
attachment = await DataResolver.resolveImage(attachment); attachment = await resolveImage(attachment);
if (!attachment) throw new DiscordjsTypeError(ErrorCodes.ReqResourceType); if (!attachment) throw new DiscordjsTypeError(ErrorCodes.ReqResourceType);
const body = { image: attachment, name }; const body = { image: attachment, name };

View File

@@ -5,7 +5,7 @@ const { Routes } = require('discord-api-types/v10');
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const { DiscordjsError, ErrorCodes } = require('../errors'); const { DiscordjsError, ErrorCodes } = require('../errors');
const Invite = require('../structures/Invite'); const Invite = require('../structures/Invite');
const DataResolver = require('../util/DataResolver'); const { resolveInviteCode } = require('../util/DataResolver');
/** /**
* Manages API methods for GuildInvites and stores their cache. * Manages API methods for GuildInvites and stores their cache.
@@ -124,7 +124,7 @@ class GuildInviteManager extends CachedManager {
fetch(options) { fetch(options) {
if (!options) return this._fetchMany(); if (!options) return this._fetchMany();
if (typeof options === 'string') { if (typeof options === 'string') {
const code = DataResolver.resolveInviteCode(options); const code = resolveInviteCode(options);
if (!code) return Promise.reject(new DiscordjsError(ErrorCodes.InviteResolveCode)); if (!code) return Promise.reject(new DiscordjsError(ErrorCodes.InviteResolveCode));
return this._fetchSingle({ code, cache: true }); return this._fetchSingle({ code, cache: true });
} }
@@ -140,7 +140,7 @@ class GuildInviteManager extends CachedManager {
} }
return this._fetchSingle({ return this._fetchSingle({
...options, ...options,
code: DataResolver.resolveInviteCode(options.code), code: resolveInviteCode(options.code),
}); });
} }
@@ -206,7 +206,7 @@ class GuildInviteManager extends CachedManager {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async delete(invite, reason) { async delete(invite, reason) {
const code = DataResolver.resolveInviteCode(invite); const code = resolveInviteCode(invite);
await this.client.rest.delete(Routes.invite(code), { reason }); await this.client.rest.delete(Routes.invite(code), { reason });
} }

View File

@@ -14,7 +14,7 @@ const { GuildMember } = require('../structures/GuildMember');
const Invite = require('../structures/Invite'); const Invite = require('../structures/Invite');
const OAuth2Guild = require('../structures/OAuth2Guild'); const OAuth2Guild = require('../structures/OAuth2Guild');
const { Role } = require('../structures/Role'); const { Role } = require('../structures/Role');
const DataResolver = require('../util/DataResolver'); const { resolveImage } = require('../util/DataResolver');
const Events = require('../util/Events'); const Events = require('../util/Events');
const PermissionsBitField = require('../util/PermissionsBitField'); const PermissionsBitField = require('../util/PermissionsBitField');
const SystemChannelFlagsBitField = require('../util/SystemChannelFlagsBitField'); const SystemChannelFlagsBitField = require('../util/SystemChannelFlagsBitField');
@@ -179,7 +179,7 @@ class GuildManager extends CachedManager {
const data = await this.client.rest.post(Routes.guilds(), { const data = await this.client.rest.post(Routes.guilds(), {
body: { body: {
name, name,
icon: icon && (await DataResolver.resolveImage(icon)), icon: icon && (await resolveImage(icon)),
verification_level: verificationLevel, verification_level: verificationLevel,
default_message_notifications: defaultMessageNotifications, default_message_notifications: defaultMessageNotifications,
explicit_content_filter: explicitContentFilter, explicit_content_filter: explicitContentFilter,

View File

@@ -6,7 +6,7 @@ const { GuildScheduledEventEntityType, Routes } = require('discord-api-types/v10
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const { DiscordjsTypeError, DiscordjsError, ErrorCodes } = require('../errors'); const { DiscordjsTypeError, DiscordjsError, ErrorCodes } = require('../errors');
const { GuildScheduledEvent } = require('../structures/GuildScheduledEvent'); const { GuildScheduledEvent } = require('../structures/GuildScheduledEvent');
const DataResolver = require('../util/DataResolver'); const { resolveImage } = require('../util/DataResolver');
/** /**
* Manages API methods for GuildScheduledEvents and stores their cache. * Manages API methods for GuildScheduledEvents and stores their cache.
@@ -103,7 +103,7 @@ class GuildScheduledEventManager extends CachedManager {
description, description,
entity_type: entityType, entity_type: entityType,
entity_metadata, entity_metadata,
image: image && (await DataResolver.resolveImage(image)), image: image && (await resolveImage(image)),
}, },
reason, reason,
}); });
@@ -222,7 +222,7 @@ class GuildScheduledEventManager extends CachedManager {
description, description,
entity_type: entityType, entity_type: entityType,
status, status,
image: image && (await DataResolver.resolveImage(image)), image: image && (await resolveImage(image)),
entity_metadata, entity_metadata,
}, },
reason, reason,

View File

@@ -6,7 +6,7 @@ const { Routes } = require('discord-api-types/v10');
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const { DiscordjsTypeError, ErrorCodes } = require('../errors'); const { DiscordjsTypeError, ErrorCodes } = require('../errors');
const { Role } = require('../structures/Role'); const { Role } = require('../structures/Role');
const DataResolver = require('../util/DataResolver'); const { resolveImage } = require('../util/DataResolver');
const PermissionsBitField = require('../util/PermissionsBitField'); const PermissionsBitField = require('../util/PermissionsBitField');
const { setPosition, resolveColor } = require('../util/Util'); const { setPosition, resolveColor } = require('../util/Util');
@@ -140,7 +140,7 @@ class RoleManager extends CachedManager {
if (permissions !== undefined) permissions = new PermissionsBitField(permissions); if (permissions !== undefined) permissions = new PermissionsBitField(permissions);
if (icon) { if (icon) {
const guildEmojiURL = this.guild.emojis.resolve(icon)?.imageURL(); const guildEmojiURL = this.guild.emojis.resolve(icon)?.imageURL();
icon = guildEmojiURL ? await DataResolver.resolveImage(guildEmojiURL) : await DataResolver.resolveImage(icon); icon = guildEmojiURL ? await resolveImage(guildEmojiURL) : await resolveImage(icon);
if (typeof icon !== 'string') icon = undefined; if (typeof icon !== 'string') icon = undefined;
} }
@@ -192,7 +192,7 @@ class RoleManager extends CachedManager {
let icon = options.icon; let icon = options.icon;
if (icon) { if (icon) {
const guildEmojiURL = this.guild.emojis.resolve(icon)?.imageURL(); const guildEmojiURL = this.guild.emojis.resolve(icon)?.imageURL();
icon = guildEmojiURL ? await DataResolver.resolveImage(guildEmojiURL) : await DataResolver.resolveImage(icon); icon = guildEmojiURL ? await resolveImage(guildEmojiURL) : await resolveImage(icon);
if (typeof icon !== 'string') icon = undefined; if (typeof icon !== 'string') icon = undefined;
} }

View File

@@ -6,7 +6,7 @@ const Team = require('./Team');
const Application = require('./interfaces/Application'); const Application = require('./interfaces/Application');
const ApplicationCommandManager = require('../managers/ApplicationCommandManager'); const ApplicationCommandManager = require('../managers/ApplicationCommandManager');
const ApplicationFlagsBitField = require('../util/ApplicationFlagsBitField'); const ApplicationFlagsBitField = require('../util/ApplicationFlagsBitField');
const DataResolver = require('../util/DataResolver'); const { resolveImage } = require('../util/DataResolver');
const PermissionsBitField = require('../util/PermissionsBitField'); const PermissionsBitField = require('../util/PermissionsBitField');
/** /**
@@ -227,8 +227,8 @@ class ClientApplication extends Application {
role_connections_verification_url: roleConnectionsVerificationURL, role_connections_verification_url: roleConnectionsVerificationURL,
install_params: installParams, install_params: installParams,
flags: flags === undefined ? undefined : ApplicationFlagsBitField.resolve(flags), flags: flags === undefined ? undefined : ApplicationFlagsBitField.resolve(flags),
icon: icon && (await DataResolver.resolveImage(icon)), icon: icon && (await resolveImage(icon)),
cover_image: coverImage && (await DataResolver.resolveImage(coverImage)), cover_image: coverImage && (await resolveImage(coverImage)),
interactions_endpoint_url: interactionsEndpointURL, interactions_endpoint_url: interactionsEndpointURL,
tags, tags,
}, },

View File

@@ -2,7 +2,7 @@
const { Routes } = require('discord-api-types/v10'); const { Routes } = require('discord-api-types/v10');
const User = require('./User'); const User = require('./User');
const DataResolver = require('../util/DataResolver'); const { resolveImage } = require('../util/DataResolver');
/** /**
* Represents the logged in client's Discord user. * Represents the logged in client's Discord user.
@@ -56,7 +56,7 @@ class ClientUser extends User {
*/ */
async edit({ username, avatar }) { async edit({ username, avatar }) {
const data = await this.client.rest.patch(Routes.user(), { const data = await this.client.rest.patch(Routes.user(), {
body: { username, avatar: avatar && (await DataResolver.resolveImage(avatar)) }, body: { username, avatar: avatar && (await resolveImage(avatar)) },
}); });
this.client.token = data.token; this.client.token = data.token;

View File

@@ -26,7 +26,7 @@ const PresenceManager = require('../managers/PresenceManager');
const RoleManager = require('../managers/RoleManager'); const RoleManager = require('../managers/RoleManager');
const StageInstanceManager = require('../managers/StageInstanceManager'); const StageInstanceManager = require('../managers/StageInstanceManager');
const VoiceStateManager = require('../managers/VoiceStateManager'); const VoiceStateManager = require('../managers/VoiceStateManager');
const DataResolver = require('../util/DataResolver'); const { resolveImage } = require('../util/DataResolver');
const Status = require('../util/Status'); const Status = require('../util/Status');
const SystemChannelFlagsBitField = require('../util/SystemChannelFlagsBitField'); const SystemChannelFlagsBitField = require('../util/SystemChannelFlagsBitField');
const { discordSort, getSortableGroupTypes, resolvePartialEmoji } = require('../util/Util'); const { discordSort, getSortableGroupTypes, resolvePartialEmoji } = require('../util/Util');
@@ -862,11 +862,11 @@ class Guild extends AnonymousGuild {
explicit_content_filter: explicitContentFilter, explicit_content_filter: explicitContentFilter,
afk_channel_id: afkChannel && this.client.channels.resolveId(afkChannel), afk_channel_id: afkChannel && this.client.channels.resolveId(afkChannel),
afk_timeout: afkTimeout, afk_timeout: afkTimeout,
icon: icon && (await DataResolver.resolveImage(icon)), icon: icon && (await resolveImage(icon)),
owner_id: owner && this.client.users.resolveId(owner), owner_id: owner && this.client.users.resolveId(owner),
splash: splash && (await DataResolver.resolveImage(splash)), splash: splash && (await resolveImage(splash)),
discovery_splash: discoverySplash && (await DataResolver.resolveImage(discoverySplash)), discovery_splash: discoverySplash && (await resolveImage(discoverySplash)),
banner: banner && (await DataResolver.resolveImage(banner)), banner: banner && (await resolveImage(banner)),
system_channel_id: systemChannel && this.client.channels.resolveId(systemChannel), system_channel_id: systemChannel && this.client.channels.resolveId(systemChannel),
system_channel_flags: system_channel_flags:
systemChannelFlags === undefined ? undefined : SystemChannelFlagsBitField.resolve(systemChannelFlags), systemChannelFlags === undefined ? undefined : SystemChannelFlagsBitField.resolve(systemChannelFlags),

View File

@@ -3,7 +3,7 @@
const { setTimeout, clearTimeout } = require('node:timers'); const { setTimeout, clearTimeout } = require('node:timers');
const { RouteBases, Routes } = require('discord-api-types/v10'); const { RouteBases, Routes } = require('discord-api-types/v10');
const Base = require('./Base'); const Base = require('./Base');
const DataResolver = require('../util/DataResolver'); const { resolveImage } = require('../util/DataResolver');
const Events = require('../util/Events'); const Events = require('../util/Events');
/** /**
@@ -126,7 +126,7 @@ class GuildTemplate extends Base {
const data = await client.rest.post(Routes.template(this.code), { const data = await client.rest.post(Routes.template(this.code), {
body: { body: {
name, name,
icon: await DataResolver.resolveImage(icon), icon: await resolveImage(icon),
}, },
}); });

View File

@@ -5,7 +5,7 @@ const { lazy, isJSONEncodable } = require('@discordjs/util');
const { MessageFlags } = require('discord-api-types/v10'); const { MessageFlags } = require('discord-api-types/v10');
const ActionRowBuilder = require('./ActionRowBuilder'); const ActionRowBuilder = require('./ActionRowBuilder');
const { DiscordjsRangeError, ErrorCodes } = require('../errors'); const { DiscordjsRangeError, ErrorCodes } = require('../errors');
const DataResolver = require('../util/DataResolver'); const { resolveFile } = require('../util/DataResolver');
const MessageFlagsBitField = require('../util/MessageFlagsBitField'); const MessageFlagsBitField = require('../util/MessageFlagsBitField');
const { basename, verifyString } = require('../util/Util'); const { basename, verifyString } = require('../util/Util');
@@ -257,7 +257,7 @@ class MessagePayload {
name = fileLike.name ?? findName(attachment); name = fileLike.name ?? findName(attachment);
} }
const { data, contentType } = await DataResolver.resolveFile(attachment); const { data, contentType } = await resolveFile(attachment);
return { data, name, contentType }; return { data, name, contentType };
} }

View File

@@ -6,7 +6,7 @@ const { DiscordSnowflake } = require('@sapphire/snowflake');
const { Routes, WebhookType } = require('discord-api-types/v10'); const { Routes, WebhookType } = require('discord-api-types/v10');
const MessagePayload = require('./MessagePayload'); const MessagePayload = require('./MessagePayload');
const { DiscordjsError, ErrorCodes } = require('../errors'); const { DiscordjsError, ErrorCodes } = require('../errors');
const DataResolver = require('../util/DataResolver'); const { resolveImage } = require('../util/DataResolver');
const getMessage = lazy(() => require('./Message').Message); const getMessage = lazy(() => require('./Message').Message);
@@ -276,7 +276,7 @@ class Webhook {
*/ */
async edit({ name = this.name, avatar, channel, reason }) { async edit({ name = this.name, avatar, channel, reason }) {
if (avatar && !(typeof avatar === 'string' && avatar.startsWith('data:'))) { if (avatar && !(typeof avatar === 'string' && avatar.startsWith('data:'))) {
avatar = await DataResolver.resolveImage(avatar); avatar = await resolveImage(avatar);
} }
channel &&= channel.id ?? channel; channel &&= channel.id ?? channel;
const data = await this.client.rest.patch(Routes.webhook(this.id, channel ? undefined : this.token), { const data = await this.client.rest.patch(Routes.webhook(this.id, channel ? undefined : this.token), {

View File

@@ -15,12 +15,19 @@ const getPartialGroupDMChannel = lazy(() => require('../structures/PartialGroupD
const getForumChannel = lazy(() => require('../structures/ForumChannel')); const getForumChannel = lazy(() => require('../structures/ForumChannel'));
const getMediaChannel = lazy(() => require('../structures/MediaChannel')); const getMediaChannel = lazy(() => require('../structures/MediaChannel'));
/**
* Extra options for creating a channel.
* @typedef {Object} CreateChannelOptions
* @property {boolean} [allowFromUnknownGuild] Whether to allow creating a channel from an unknown guild
* @private
*/
/** /**
* Creates a discord.js channel from data received from the API. * Creates a discord.js channel from data received from the API.
* @param {Client} client The client * @param {Client} client The client
* @param {APIChannel} data The data of the channel to create * @param {APIChannel} data The data of the channel to create
* @param {Guild} [guild] The guild where this channel belongs * @param {Guild} [guild] The guild where this channel belongs
* @param {Object} [extras] Extra information to supply for creating this channel * @param {CreateChannelOptions} [extras] Extra information to supply for creating this channel
* @returns {BaseChannel} Any kind of channel. * @returns {BaseChannel} Any kind of channel.
* @ignore * @ignore
*/ */

View File

@@ -8,133 +8,134 @@ const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors');
const Invite = require('../structures/Invite'); const Invite = require('../structures/Invite');
/** /**
* The DataResolver identifies different objects and tries to resolve a specific piece of information from them. * Data that can be resolved to give an invite code. This can be:
* * An invite code
* * An invite URL
* @typedef {string} InviteResolvable
*/
/**
* Data that can be resolved to give a template code. This can be:
* * A template code
* * A template URL
* @typedef {string} GuildTemplateResolvable
*/
/**
* Resolves the string to a code based on the passed regex.
* @param {string} data The string to resolve
* @param {RegExp} regex The RegExp used to extract the code
* @returns {string}
* @private * @private
*/ */
class DataResolver extends null { function resolveCode(data, regex) {
/** return regex.exec(data)?.[1] ?? data;
* Data that can be resolved to give an invite code. This can be:
* * An invite code
* * An invite URL
* @typedef {string} InviteResolvable
*/
/**
* Data that can be resolved to give a template code. This can be:
* * A template code
* * A template URL
* @typedef {string} GuildTemplateResolvable
*/
/**
* Resolves the string to a code based on the passed regex.
* @param {string} data The string to resolve
* @param {RegExp} regex The RegExp used to extract the code
* @returns {string}
*/
static resolveCode(data, regex) {
return regex.exec(data)?.[1] ?? data;
}
/**
* Resolves InviteResolvable to an invite code.
* @param {InviteResolvable} data The invite resolvable to resolve
* @returns {string}
*/
static resolveInviteCode(data) {
return this.resolveCode(data, Invite.InvitesPattern);
}
/**
* Resolves GuildTemplateResolvable to a template code.
* @param {GuildTemplateResolvable} data The template resolvable to resolve
* @returns {string}
*/
static resolveGuildTemplateCode(data) {
const GuildTemplate = require('../structures/GuildTemplate');
return this.resolveCode(data, GuildTemplate.GuildTemplatesPattern);
}
/**
* Resolves a Base64Resolvable, a string, or a BufferResolvable to a Base 64 image.
* @param {BufferResolvable|Base64Resolvable} image The image to be resolved
* @returns {Promise<?string>}
*/
static async resolveImage(image) {
if (!image) return null;
if (typeof image === 'string' && image.startsWith('data:')) {
return image;
}
const file = await this.resolveFile(image);
return this.resolveBase64(file.data);
}
/**
* Data that resolves to give a Base64 string, typically for image uploading. This can be:
* * A Buffer
* * A base64 string
* @typedef {Buffer|string} Base64Resolvable
*/
/**
* Resolves a Base64Resolvable to a Base 64 image.
* @param {Base64Resolvable} data The base 64 resolvable you want to resolve
* @returns {?string}
*/
static resolveBase64(data) {
if (Buffer.isBuffer(data)) return `data:image/jpg;base64,${data.toString('base64')}`;
return data;
}
/**
* Data that can be resolved to give a Buffer. This can be:
* * A Buffer
* * The path to a local file
* * A URL <warn>When provided a URL, discord.js will fetch the URL internally in order to create a Buffer.
* This can pose a security risk when the URL has not been sanitized</warn>
* @typedef {string|Buffer} BufferResolvable
*/
/**
* @external Stream
* @see {@link https://nodejs.org/api/stream.html}
*/
/**
* @typedef {Object} ResolvedFile
* @property {Buffer} data Buffer containing the file data
* @property {string} [contentType] Content type of the file
*/
/**
* Resolves a BufferResolvable to a Buffer.
* @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
* @returns {Promise<ResolvedFile>}
*/
static async resolveFile(resource) {
if (Buffer.isBuffer(resource)) return { data: resource };
if (typeof resource[Symbol.asyncIterator] === 'function') {
const buffers = [];
for await (const data of resource) buffers.push(Buffer.from(data));
return { data: Buffer.concat(buffers) };
}
if (typeof resource === 'string') {
if (/^https?:\/\//.test(resource)) {
const res = await fetch(resource);
return { data: Buffer.from(await res.arrayBuffer()), contentType: res.headers.get('content-type') };
}
const file = path.resolve(resource);
const stats = await fs.stat(file);
if (!stats.isFile()) throw new DiscordjsError(ErrorCodes.FileNotFound, file);
return { data: await fs.readFile(file) };
}
throw new DiscordjsTypeError(ErrorCodes.ReqResourceType);
}
} }
module.exports = DataResolver; /**
* Resolves InviteResolvable to an invite code.
* @param {InviteResolvable} data The invite resolvable to resolve
* @returns {string}
* @private
*/
function resolveInviteCode(data) {
return resolveCode(data, Invite.InvitesPattern);
}
/**
* Resolves GuildTemplateResolvable to a template code.
* @param {GuildTemplateResolvable} data The template resolvable to resolve
* @returns {string}
* @private
*/
function resolveGuildTemplateCode(data) {
const GuildTemplate = require('../structures/GuildTemplate');
return resolveCode(data, GuildTemplate.GuildTemplatesPattern);
}
/**
* Data that can be resolved to give a Buffer. This can be:
* * A Buffer
* * The path to a local file
* * A URL <warn>When provided a URL, discord.js will fetch the URL internally in order to create a Buffer.
* This can pose a security risk when the URL has not been sanitized</warn>
* @typedef {string|Buffer} BufferResolvable
*/
/**
* @external Stream
* @see {@link https://nodejs.org/api/stream.html}
*/
/**
* @typedef {Object} ResolvedFile
* @property {Buffer} data Buffer containing the file data
* @property {string} [contentType] Content-Type of the file
* @private
*/
/**
* Resolves a BufferResolvable to a Buffer.
* @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
* @returns {Promise<ResolvedFile>}
* @private
*/
async function resolveFile(resource) {
if (Buffer.isBuffer(resource)) return { data: resource };
if (typeof resource[Symbol.asyncIterator] === 'function') {
const buffers = [];
for await (const data of resource) buffers.push(Buffer.from(data));
return { data: Buffer.concat(buffers) };
}
if (typeof resource === 'string') {
if (/^https?:\/\//.test(resource)) {
const res = await fetch(resource);
return { data: Buffer.from(await res.arrayBuffer()), contentType: res.headers.get('content-type') };
}
const file = path.resolve(resource);
const stats = await fs.stat(file);
if (!stats.isFile()) throw new DiscordjsError(ErrorCodes.FileNotFound, file);
return { data: await fs.readFile(file) };
}
throw new DiscordjsTypeError(ErrorCodes.ReqResourceType);
}
/**
* Data that resolves to give a Base64 string, typically for image uploading. This can be:
* * A Buffer
* * A base64 string
* @typedef {Buffer|string} Base64Resolvable
*/
/**
* Resolves a Base64Resolvable to a Base 64 image.
* @param {Base64Resolvable} data The base 64 resolvable you want to resolve
* @returns {?string}
* @private
*/
function resolveBase64(data) {
if (Buffer.isBuffer(data)) return `data:image/jpg;base64,${data.toString('base64')}`;
return data;
}
/**
* Resolves a Base64Resolvable, a string, or a BufferResolvable to a Base 64 image.
* @param {BufferResolvable|Base64Resolvable} image The image to be resolved
* @returns {Promise<?string>}
* @private
*/
async function resolveImage(image) {
if (!image) return null;
if (typeof image === 'string' && image.startsWith('data:')) {
return image;
}
const file = await resolveFile(image);
return resolveBase64(file.data);
}
module.exports = { resolveCode, resolveInviteCode, resolveGuildTemplateCode, resolveImage, resolveBase64, resolveFile };

View File

@@ -128,6 +128,7 @@ function resolvePartialEmoji(emoji) {
* @property {string} name Error type * @property {string} name Error type
* @property {string} message Message for the error * @property {string} message Message for the error
* @property {string} stack Stack for the error * @property {string} stack Stack for the error
* @private
*/ */
/** /**

View File

@@ -2,10 +2,10 @@
/* eslint-env jest */ /* eslint-env jest */
const { DataResolver } = require('../src'); const { resolveGuildTemplateCode } = require('../src');
describe('resolveGuildTemplateCode', () => { describe('resolveGuildTemplateCode', () => {
test('basic', () => { test('basic', () => {
expect(DataResolver.resolveGuildTemplateCode('https://discord.new/abc')).toEqual('abc'); expect(resolveGuildTemplateCode('https://discord.new/abc')).toEqual('abc');
}); });
}); });

View File

@@ -1278,21 +1278,12 @@ export class ContextMenuCommandInteraction<Cached extends CacheType = CacheType>
private resolveContextMenuOptions(data: APIApplicationCommandInteractionData): CommandInteractionOption<Cached>[]; private resolveContextMenuOptions(data: APIApplicationCommandInteractionData): CommandInteractionOption<Cached>[];
} }
/** @internal */
export interface ResolvedFile { export interface ResolvedFile {
data: Buffer; data: Buffer;
contentType?: string; contentType?: string;
} }
export class DataResolver extends null {
private constructor();
public static resolveBase64(data: Base64Resolvable): string;
public static resolveCode(data: string, regex: RegExp): string;
public static resolveFile(resource: BufferResolvable | Stream): Promise<ResolvedFile>;
public static resolveImage(resource: BufferResolvable | Base64Resolvable): Promise<string | null>;
public static resolveInviteCode(data: InviteResolvable): string;
public static resolveGuildTemplateCode(data: GuildTemplateResolvable): string;
}
export class DMChannel extends TextBasedChannelMixin(BaseChannel, false, [ export class DMChannel extends TextBasedChannelMixin(BaseChannel, false, [
'bulkDelete', 'bulkDelete',
'fetchWebhooks', 'fetchWebhooks',
@@ -3266,6 +3257,7 @@ export class UserFlagsBitField extends BitField<UserFlagsString> {
public static resolve(bit?: BitFieldResolvable<UserFlagsString, number>): number; public static resolve(bit?: BitFieldResolvable<UserFlagsString, number>): number;
} }
/** @internal */
export function basename(path: string, ext?: string): string; export function basename(path: string, ext?: string): string;
export function cleanContent(str: string, channel: TextBasedChannel): string; export function cleanContent(str: string, channel: TextBasedChannel): string;
export function discordSort<Key, Value extends { rawPosition: number; id: Snowflake }>( export function discordSort<Key, Value extends { rawPosition: number; id: Snowflake }>(
@@ -3274,14 +3266,19 @@ export function discordSort<Key, Value extends { rawPosition: number; id: Snowfl
export function cleanCodeBlockContent(text: string): string; export function cleanCodeBlockContent(text: string): string;
export function fetchRecommendedShardCount(token: string, options?: FetchRecommendedShardCountOptions): Promise<number>; export function fetchRecommendedShardCount(token: string, options?: FetchRecommendedShardCountOptions): Promise<number>;
export function flatten(obj: unknown, ...props: Record<string, boolean | string>[]): unknown; export function flatten(obj: unknown, ...props: Record<string, boolean | string>[]): unknown;
/** @internal */
export function makeError(obj: MakeErrorOptions): Error; export function makeError(obj: MakeErrorOptions): Error;
/** @internal */
export function makePlainError(err: Error): MakeErrorOptions; export function makePlainError(err: Error): MakeErrorOptions;
/** @internal */
export function moveElementInArray(array: unknown[], element: unknown, newIndex: number, offset?: boolean): number; export function moveElementInArray(array: unknown[], element: unknown, newIndex: number, offset?: boolean): number;
export function parseEmoji(text: string): PartialEmoji | null; export function parseEmoji(text: string): PartialEmoji | null;
export function resolveColor(color: ColorResolvable): number; export function resolveColor(color: ColorResolvable): number;
/** @internal */
export function resolvePartialEmoji(emoji: Snowflake): PartialEmojiOnlyId; export function resolvePartialEmoji(emoji: Snowflake): PartialEmojiOnlyId;
export function resolvePartialEmoji(emoji: Emoji | EmojiIdentifierResolvable): PartialEmoji | null; export function resolvePartialEmoji(emoji: Emoji | EmojiIdentifierResolvable): PartialEmoji | null;
export function verifyString(data: string, error?: typeof Error, errorMessage?: string, allowEmpty?: boolean): string; export function verifyString(data: string, error?: typeof Error, errorMessage?: string, allowEmpty?: boolean): string;
/** @internal */
export function setPosition<Item extends Channel | Role>( export function setPosition<Item extends Channel | Role>(
item: Item, item: Item,
position: number, position: number,
@@ -3292,6 +3289,7 @@ export function setPosition<Item extends Channel | Role>(
reason?: string, reason?: string,
): Promise<{ id: Snowflake; position: number }[]>; ): Promise<{ id: Snowflake; position: number }[]>;
export function parseWebhookURL(url: string): WebhookClientDataIdWithToken | null; export function parseWebhookURL(url: string): WebhookClientDataIdWithToken | null;
/** @internal */
export function transformResolved<Cached extends CacheType>( export function transformResolved<Cached extends CacheType>(
supportingData: SupportingInteractionResolvedData, supportingData: SupportingInteractionResolvedData,
data?: APIApplicationCommandInteractionData['resolved'], data?: APIApplicationCommandInteractionData['resolved'],
@@ -3319,11 +3317,18 @@ export interface MappedComponentTypes {
[ComponentType.TextInput]: TextInputComponent; [ComponentType.TextInput]: TextInputComponent;
} }
export interface ChannelCreateOptions { /** @internal */
export interface CreateChannelOptions {
allowFromUnknownGuild?: boolean; allowFromUnknownGuild?: boolean;
} }
export function createChannel(client: Client<true>, data: APIChannel, options?: ChannelCreateOptions): Channel; /** @internal */
export function createChannel(
client: Client<true>,
data: APIChannel,
guild?: Guild,
extras?: CreateChannelOptions,
): Channel;
export function createComponent<Type extends keyof MappedComponentTypes>( export function createComponent<Type extends keyof MappedComponentTypes>(
data: APIMessageComponent & { type: Type }, data: APIMessageComponent & { type: Type },
@@ -3374,6 +3379,19 @@ export class Formatters extends null {
public static userMention: typeof userMention; public static userMention: typeof userMention;
} }
/** @internal */
export function resolveBase64(data: Base64Resolvable): string;
/** @internal */
export function resolveCode(data: string, regex: RegExp): string;
/** @internal */
export function resolveFile(resource: BufferResolvable | Stream): Promise<ResolvedFile>;
/** @internal */
export function resolveImage(resource: BufferResolvable | Base64Resolvable): Promise<string | null>;
/** @internal */
export function resolveInviteCode(data: InviteResolvable): string;
/** @internal */
export function resolveGuildTemplateCode(data: GuildTemplateResolvable): string;
export type ComponentData = export type ComponentData =
| MessageActionRowComponentData | MessageActionRowComponentData
| ModalActionRowComponentData | ModalActionRowComponentData
@@ -3816,11 +3834,13 @@ export enum DiscordjsErrorCodes {
GuildForumMessageRequired = 'GuildForumMessageRequired', GuildForumMessageRequired = 'GuildForumMessageRequired',
} }
/** @internal */
export interface DiscordjsErrorFields<Name extends string> { export interface DiscordjsErrorFields<Name extends string> {
readonly name: `${Name} [${DiscordjsErrorCodes}]`; readonly name: `${Name} [${DiscordjsErrorCodes}]`;
get code(): DiscordjsErrorCodes; get code(): DiscordjsErrorCodes;
} }
/** @internal */
export function DiscordjsErrorMixin<Entity, Name extends string>( export function DiscordjsErrorMixin<Entity, Name extends string>(
Base: Constructable<Entity>, Base: Constructable<Entity>,
name: Name, name: Name,
@@ -4369,10 +4389,13 @@ export class VoiceStateManager extends CachedManager<Snowflake, VoiceState, type
// to each of those classes // to each of those classes
export type Constructable<Entity> = abstract new (...args: any[]) => Entity; export type Constructable<Entity> = abstract new (...args: any[]) => Entity;
/** @internal */
export function PartialTextBasedChannel<Entity>( export function PartialTextBasedChannel<Entity>(
Base?: Constructable<Entity>, Base?: Constructable<Entity>,
): Constructable<Entity & PartialTextBasedChannelFields<false>>; ): Constructable<Entity & PartialTextBasedChannelFields<false>>;
/** @internal */
export function TextBasedChannelMixin< export function TextBasedChannelMixin<
Entity, Entity,
InGuild extends boolean = boolean, InGuild extends boolean = boolean,
@@ -4413,9 +4436,12 @@ export interface TextBasedChannelFields<InGuild extends boolean = boolean>
setNSFW(nsfw?: boolean, reason?: string): Promise<this>; setNSFW(nsfw?: boolean, reason?: string): Promise<this>;
} }
/** @internal */
export function PartialWebhookMixin<Entity>(Base?: Constructable<Entity>): Constructable<Entity & PartialWebhookFields>; export function PartialWebhookMixin<Entity>(Base?: Constructable<Entity>): Constructable<Entity & PartialWebhookFields>;
/** @internal */
export function WebhookMixin<Entity>(Base?: Constructable<Entity>): Constructable<Entity & WebhookFields>; export function WebhookMixin<Entity>(Base?: Constructable<Entity>): Constructable<Entity & WebhookFields>;
/** @internal */
export interface PartialWebhookFields { export interface PartialWebhookFields {
id: Snowflake; id: Snowflake;
get url(): string; get url(): string;
@@ -4430,6 +4456,7 @@ export interface PartialWebhookFields {
): Promise<APIMessage | Message>; ): Promise<APIMessage | Message>;
} }
/** @internal */
export interface WebhookFields extends PartialWebhookFields { export interface WebhookFields extends PartialWebhookFields {
get createdAt(): Date; get createdAt(): Date;
get createdTimestamp(): number; get createdTimestamp(): number;
@@ -5979,6 +6006,7 @@ export interface LifetimeFilterOptions<Key, Value> {
lifetime?: number; lifetime?: number;
} }
/** @internal */
export interface MakeErrorOptions { export interface MakeErrorOptions {
name: string; name: string;
message: string; message: string;
@@ -6433,6 +6461,7 @@ export interface StageInstanceEditOptions {
privacyLevel?: StageInstancePrivacyLevel; privacyLevel?: StageInstancePrivacyLevel;
} }
/** @internal */
export interface SupportingInteractionResolvedData { export interface SupportingInteractionResolvedData {
client: Client; client: Client;
guild?: Guild; guild?: Guild;