/* * 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 . */ 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, };