etcher/lib/shared/errors.ts
Alexis Svinartchouk f2a37079eb Don't use lodash in child-writer.js
Changelog-entry: Don't use lodash in child-writer.js
Change-type: patch
2020-08-06 15:40:42 +02:00

224 lines
5.1 KiB
TypeScript

/*
* Copyright 2016 balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type ErrorWithPath = Error & {
path?: string;
code?: keyof typeof HUMAN_FRIENDLY;
};
/**
* @summary Human-friendly error messages
*/
export const HUMAN_FRIENDLY = {
ENOENT: {
title: (error: ErrorWithPath) => {
return `No such file or directory: ${error.path}`;
},
description: () => "The file you're trying to access doesn't exist",
},
EPERM: {
title: () => "You're not authorized to perform this operation",
description: () =>
'Please ensure you have necessary permissions for this task',
},
EACCES: {
title: () => "You don't have access to this resource",
description: () =>
'Please ensure you have necessary permissions to access this resource',
},
ENOMEM: {
title: () => 'Your system ran out of memory',
description: () =>
'Please make sure your system has enough available memory for this task',
},
} as const;
/**
* @summary Get user friendly property from an error
*
* @example
* const error = new Error('My error');
* error.code = 'ENOMEM';
*
* const friendlyDescription = getUserFriendlyMessageProperty(error, 'description');
*
* if (friendlyDescription) {
* console.log(friendlyDescription);
* }
*/
function getUserFriendlyMessageProperty(
error: ErrorWithPath,
property: 'title' | 'description',
): string | undefined {
if (typeof error.code !== 'string') {
return undefined;
}
return HUMAN_FRIENDLY[error.code]?.[property]?.(error);
}
function isBlank(s: string | number | null | undefined) {
if (typeof s === 'number') {
s = s.toString();
}
return (s ?? '').trim() === '';
}
/**
* @summary Get the title of an error
*
* @description
* Try to get as much information as possible about the error
* rather than falling back to generic messages right away.
*/
export function getTitle(error: ErrorWithPath): string {
const codeTitle = getUserFriendlyMessageProperty(error, 'title');
if (codeTitle !== undefined) {
return codeTitle;
}
const message = error.message;
if (!isBlank(message)) {
return message;
}
const code = error.code;
if (!isBlank(code)) {
return `Error code: ${code}`;
}
return 'An error ocurred';
}
/**
* @summary Get the description of an error
*/
export function getDescription(
error: ErrorWithPath & { description?: string },
): string {
if (!isBlank(error.description)) {
return error.description as string;
}
const codeDescription = getUserFriendlyMessageProperty(error, 'description');
if (codeDescription !== undefined) {
return codeDescription;
}
if (error.stack) {
return error.stack;
}
return JSON.stringify(error, null, 2);
}
/**
* @summary Create an error
*/
export function createError(options: {
title: string;
description?: string;
report?: boolean;
code?: keyof typeof HUMAN_FRIENDLY;
}): ErrorWithPath & { description?: string; report?: boolean } {
if (isBlank(options.title)) {
throw new Error(`Invalid error title: ${options.title}`);
}
const error: ErrorWithPath & {
description?: string;
report?: boolean;
code?: string;
} = new Error(options.title);
error.description = options.description;
if (options.report === false) {
error.report = false;
}
if (options.code !== undefined) {
error.code = options.code;
}
return error;
}
/**
* @summary Create a user error
*
* @description
* User errors represent invalid states that the user
* caused, that are not errors on the application itself.
* Therefore, user errors don't get reported to analytics
* and error reporting services.
*/
export function createUserError(options: {
title: string;
description: string;
code?: keyof typeof HUMAN_FRIENDLY;
}): Error {
return createError({
title: options.title,
description: options.description,
report: false,
code: options.code,
});
}
/**
* @summary Convert an Error object to a JSON object
* @function
* @public
*
* @param {Error} error - error object
* @returns {Object} json error
*
* @example
* const error = errors.toJSON(new Error('foo'))
*
* console.log(error.message);
* > 'foo'
*/
export function toJSON(
error: Error & {
description?: string;
report?: boolean;
code?: string;
syscall?: string;
errno?: number;
stdout?: string;
stderr?: string;
device?: string;
},
): any {
return {
name: error.name,
message: error.message,
description: error.description,
stack: error.stack,
report: error.report,
code: error.code,
syscall: error.syscall,
errno: error.errno,
stdout: error.stdout,
stderr: error.stderr,
device: error.device,
};
}
/**
* @summary Convert a JSON object to an Error object
*/
export function fromJSON(json: any): Error {
return Object.assign(new Error(json.message), json);
}