Files
puter/packages/backend/src/errors/error_help_details.js
T
2024-03-30 19:08:03 -04:00

245 lines
7.0 KiB
JavaScript

/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const { quot, osclink } = require("../util/strutil");
const reused = {
runtime_env_references: [
{
subject: 'ENVIRONMENT.md file',
location: 'root of the repository',
use: 'describes which paths are checked',
},
{
subject: 'boot logger',
location: 'above this text',
use: 'shows what checks were performed',
},
{
subject: 'RuntimeEnvironment.js',
location: 'src/boot/ in repository',
use: 'code that performs the checks',
}
]
};
const programmer_errors = [
'Assignment to constant variable.'
];
const error_help_details = [
{
match: ({ message }) => (
message.startsWith('No suitable path found for')
),
apply (more) {
more.references = [
...reused.runtime_env_references,
]
}
},
{
match: ({ message }) => (
message.match(/^No (read|write) permission for/)
),
apply (more) {
more.solutions = [
{
title: 'Change permissions with chmod',
},
{
title: 'Remove the path to use working directory',
},
{
title: 'Set CONFIG_PATH or RUNTIME_PATH environment variable',
},
],
more.references = [
...reused.runtime_env_references,
]
}
},
{
match: ({ message }) => (
message.startsWith('No valid config file found in path')
),
apply (more) {
more.solutions = [
{
title: 'Create a valid config file',
},
]
}
},
{
match: ({ message }) => (
message === `config_name is required`
),
apply (more) {
more.solutions = [
'ensure config_name is present in your config file',
'Seek help on ' + osclink(
'https://discord.gg/PQcx7Teh8u',
'our Discord server'
),
];
}
},
{
match: ({ message }) => (
message == 'Assignment to constant variable.'
),
apply (more) {
more.references = [
{
subject: 'MDN Reference for this error',
location: 'on the internet',
use: 'describes why this error occurs',
url: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_const_assignment'
},
]
}
},
{
match: ({ message }) => (
programmer_errors.includes(message)
),
apply (more) {
more.notes = [
'It looks like this might be our fault.',
]
more.solutions = [
{
title: `Check for an issue on ` +
osclink('https://github.com/HeyPuter/puter/issues')
},
{
title: `If there's no issue, please ` +
osclink(
'https://github.com/HeyPuter/puter/issues/new',
'create one'
) + '.'
}
]
}
},
{
match: ({ message }) => (
message.startsWith('Expected double-quoted property')
),
apply (more) {
more.notes = [
'There might be a trailing-comma in your config',
];
}
}
];
/**
* Print error help information to a stream in a human-readable format.
*
* @param {Error} err - The error to print help for.
* @param {*} out - The stream to print to; defaults to process.stdout.
* @returns {undefined}
*/
const print_error_help = (err, out = process.stdout) => {
if ( ! err.more ) {
err.more = {};
err.more.references = [];
err.more.solutions = [];
for ( const detail of error_help_details ) {
if ( detail.match(err) ) {
detail.apply(err.more);
}
}
}
let write = out.write.bind(out);
write('\n');
const wrap_msg = s =>
`\x1B[31;1m┏━━ [ HELP:\x1B[0m ${quot(s)} \x1B[31;1m]\x1B[0m`;
const wrap_list_title = s =>
`\x1B[36;1m${s}:\x1B[0m`;
write(wrap_msg(err.message) + '\n');
write = (s) => out.write('\x1B[31;1m┃\x1B[0m ' + s);
const vis = (stok, etok, str) => {
return `\x1B[36;1m${stok}\x1B[0m${str}\x1B[36;1m${etok}\x1B[0m`;
}
let lf_sep = false;
write('Whoops! Looks like something isn\'t working!\n');
let any_help = false;
if ( err.more.notes ) {
write('\n');
lf_sep = true;
any_help = true;
for ( const note of err.more.notes ) {
write(`\x1B[33;1m * ${note}\x1B[0m\n`);
}
}
if ( err.more.solutions?.length > 0 ) {
if ( lf_sep ) write('\n');
lf_sep = true;
any_help = true;
write('The suggestions below may help resolve this issue.\n')
write('\n');
write(wrap_list_title('Possible Solutions') + '\n');
for ( const sol of err.more.solutions ) {
write(` - ${sol.title}\n`);
}
}
if ( err.more.references?.length > 0 ) {
if ( lf_sep ) write('\n');
lf_sep = true;
any_help = true;
write('The references below may be related to this issue.\n')
write('\n');
write(wrap_list_title('References') + '\n');
for ( const ref of err.more.references ) {
write(` - ${vis('[', ']', ref.subject)} ` +
`${vis('(', ')', ref.location)};\n`);
write(` ${ref.use}\n`);
if ( ref.url ) {
write(` ${osclink(ref.url)}\n`);
}
}
}
if ( ! any_help ) {
write('No help is available for this error.\n');
write('Help can be added in src/errors/error_help_details.\n');
}
out.write(`\x1B[31;1m┗━━ [ END HELP ]\x1B[0m\n`)
out.write('\n');
}
module.exports = {
error_help_details,
print_error_help,
};