Errors Standardization (#1246)

* errors and stuff

* more errors

* all the errors

* fix build
This commit is contained in:
Gus Caplan
2017-06-25 12:48:05 -05:00
committed by Amish Shah
parent 602fe06f88
commit 63e54982f4
28 changed files with 258 additions and 102 deletions

65
src/errors/DJSError.js Normal file
View File

@@ -0,0 +1,65 @@
// Heavily inspired by node's `internal/errors` module
const kCode = Symbol('code');
const messages = new Map();
const assert = require('assert');
const util = require('util');
/**
* Extend an error of some sort into a DiscordjsError
* @param {Error} Base Base error to extend
* @returns {DiscordjsError}
*/
function makeDiscordjsError(Base) {
return class DiscordjsError extends Base {
constructor(key, ...args) {
super(message(key, args));
this[kCode] = key;
if (Error.captureStackTrace) Error.captureStackTrace(this, DiscordjsError);
}
get name() {
return `${super.name} [${this[kCode]}]`;
}
get code() {
return this[kCode];
}
};
}
/**
* Format the message for an error
* @param {string} key Error key
* @param {Array<*>} args Arguments to pass for util format or as function args
* @returns {string} Formatted string
*/
function message(key, args) {
assert.strictEqual(typeof key, 'string');
const msg = messages.get(key);
assert(msg, `An invalid error message key was used: ${key}.`);
let fmt = util.format;
if (typeof msg === 'function') {
fmt = msg;
} else {
if (args === undefined || args.length === 0) return msg;
args.unshift(msg);
}
return String(fmt(...args));
}
/**
* Register an error code and message
* @param {string} sym Unique name for the error
* @param {*} val Value of the error
*/
function register(sym, val) {
messages.set(sym, typeof val === 'function' ? val : String(val));
}
module.exports = {
register,
Error: makeDiscordjsError(Error),
TypeError: makeDiscordjsError(TypeError),
RangeError: makeDiscordjsError(RangeError),
};

84
src/errors/Messages.js Normal file
View File

@@ -0,0 +1,84 @@
const { register } = require('./DJSError');
const Messages = {
CLIENT_INVALID_OPTION: (prop, must) => `The ${prop} option must be ${must}`,
TOKEN_INVALID: 'An invalid token was provided.',
TOKEN_MISSING: 'Request to use token, but token was unavailable to the client.',
FEATURE_BOT_ONLY: 'Only bot accounts are able to make use of this feature.',
FEATURE_USER_ONLY: 'Only user accounts are able to make use of this feature.',
WS_BAD_MESSAGE: 'A bad message was received from the websocket; either bad compression, or not JSON.',
WS_CONNECTION_EXISTS: 'There is already an existing WebSocket connection.',
WS_NOT_OPEN: (data = 'data') => `Websocket not open to send ${data}`,
PERMISSIONS_INVALID: 'Invalid permission string or number.',
PERMISSIONS_INVALID_FLAG: 'Invalid bitfield flag string or number',
RATELIMIT_INVALID_METHOD: 'Unknown rate limiting method.',
SHARDING_INVALID: 'Invalid shard settings were provided.',
SHARDING_REQUIRED: 'This session would have handled too many guilds - Sharding is required.',
SHARDING_CHILD_CONNECTION: 'Failed to send message to shard\'s process.',
SHARDING_PARENT_CONNECTION: 'Failed to send message to master process.',
SHARDING_NO_SHARDS: 'No shards have been spawned',
SHARDING_IN_PROCESS: 'Shards are still being spawned',
SHARDING_ALREADY_SPAWNED: count => `Already spawned ${count} shards`,
COLOR_RANGE: 'Color must be within the range 0 - 16777215 (0xFFFFFF).',
COLOR_CONVERT: 'Unable to convert color to a number.',
EMBED_FIELD_COUNT: 'MessageEmbeds may not exceed 25 fields.',
EMBED_FIELD_NAME: 'MessageEmbed field names may not exceed 256 characters or be empty.',
EMBED_FIELD_VALUE: 'MessageEmbed field values may not exceed 1024 characters or be empty.',
EMBED_FILE_LIMIT: 'You may not upload more than one file at once.',
EMBED_DESCRIPTION: 'MessageEmbed descriptions may not exceed 2048 characters.',
EMBED_FOOTER_TEXT: 'MessageEmbed footer text may not exceed 2048 characters.',
EMBED_TITLE: 'MessageEmbed titles may not exceed 256 characters.',
FILE_NOT_FOUND: file => `File could not be found: ${file}`,
USER_STATUS: 'User status must be a string',
SHARD_MESSAGE_FAILED: 'Failed to send message to master process.',
VOICE_INVALID_HEARTBEAT: 'Tried to set voice heartbeat but no valid interval was specified.',
VOICE_USER_MISSING: 'Couldn\'t resolve the user to create stream.',
VOICE_STREAM_EXISTS: 'There is already an existing stream for that user.',
VOICE_JOIN_CHANNEL: (full = false) =>
`You do not have permission to join this voice channel${full ? '; it is full.' : '.'}`,
OPUS_ENGINE_MISSING: 'Couldn\'t find an Opus engine.',
UDP_SEND_FAIL: 'Tried to send a UDP packet, but there is no socket available.',
UDP_ADDRESS_MALFORMED: 'Malformed UDP address or port.',
UDP_CONNECTION_EXISTS: 'There is already an existing UDP connection.',
REQ_BODY_TYPE: 'The response body isn\'t a Buffer.',
REQ_RESOURCE_TYPE: 'The resource must be a string or Buffer.',
IMAGE_FORMAT: format => `Invalid image format: ${format}`,
IMAGE_SIZE: size => `Invalid image size: ${size}`,
MESSAGE_MISSING: 'Message not found',
MESSAGE_BULK_DELETE_TYPE: 'The messages must be an Array, Collection, or number.',
MESSAGE_NONCE_TYPE: 'Message nonce must fit in an unsigned 64-bit integer.',
TYPING_COUNT: 'Count must be at least 1',
SPLIT_MAX_LEN: 'Message exceeds the max length and contains no split characters.',
BAN_RESOLVE_ID: 'Couldn\'t resolve the user ID to unban.',
PRUNE_DAYS_TYPE: 'Days must be a number',
SEARCH_CHANNEL_TYPE: 'Target must be a TextChannel, DMChannel, GroupDMChannel, or Guild.',
MESSAGE_SPLIT_MISSING: 'Message exceeds the max length and contains no split characters.',
GUILD_CHANNEL_RESOLVE: 'Could not resolve channel to a guild channel.',
EMOJI_TYPE: 'Emoji must be a string or Emoji/ReactionEmoji',
};
for (const [name, message] of Object.entries(Messages)) register(name, message);

2
src/errors/index.js Normal file
View File

@@ -0,0 +1,2 @@
module.exports = require('./DJSError');
module.exports.Messages = require('./Messages');