mirror of
https://github.com/balena-io/etcher.git
synced 2025-04-21 22:07:18 +00:00
224 lines
5.1 KiB
TypeScript
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);
|
|
}
|