UNPKG

base64-advanced-cli

Version:

Command line interface for advanced base 64 encoding/decoding

170 lines (169 loc) 6.76 kB
#!/usr/bin/env node import { exit } from "process"; import chalk from "chalk"; import fs from "fs"; import { program } from "commander"; import figlet from 'figlet'; import { fileURLToPath } from 'url'; import path from 'path'; import updateNotifier from 'update-notifier'; import base64ImageMime from 'base64-image-mime'; import getStdin from "get-stdin"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // get the app version const packageJsonPath = path.resolve(__dirname, '../package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString()); program .option('-d, --decode [data]', 'set mode to encoding') .option('-e, --encode [data]', 'set mode to decoding') .option('--web-safe', 'use websafe substitution characters when encoding/decoding') .option('-i, --input-file <filename>', 'read input from a file') .option('-o, --output-file <filename>', 'write output to a file') .option('-v, --version', 'display the version of this CLI') .option('--jwt <data>', 'display the content of a jwt token') .option('--html', 'encode an image into an html tag containing base64 data') .option('--no-update-notification', 'do not display update notifications') .addHelpText("before", chalk.red(figlet.textSync("b64", { font: 'Univers' }))) .addHelpText("before", chalk.green("base64-advanced-cli v" + packageJson.version)); program.parse(); const options = program.opts(); try { if (options.encode === true && !options.inputFile) { options.encode = await getStdin(); } if (options.decode === true && !options.inputFile) { options.decode = await getStdin(); } //handle update notifications if (options.updateNotification) { const updateCheckInterval = 1000 * 60 * 60 * 24; // 1 DAY const notifier = updateNotifier({ pkg: packageJson, updateCheckInterval }); if (options.version) { try { //notifier.update = await notifier.fetchInfo(); } catch (e) { console.log('Update check failed. Check your internet connection.'); } } notifier.notify(); } // if not arguments, we display help & exit the program if (!process.argv.slice(2).length) { program.outputHelp(); exit(); } // display version handling if (options.version) { console.log(chalk.green("base64-advanced-cli v" + packageJson.version)); exit(); } //////////////////// // error handling //////////////////// if (options.encode && options.decode) { throw new Error('Encode and Decode flags can\'t be used at the same time.'); } if (!options.encode && !options.decode && !options.jwt && !options.version) { throw new Error('No action to perform.'); } if (options.inputFile && options.encode && options.encode !== true) { throw new Error('Encode data and input-file can\'t be both provided at the same time.'); } if (options.inputFile && options.decode && options.decode !== true) { throw new Error('Decode data and input-file can\'t be both provided at the same time'); } if (options.html && options.decode) { throw new Error('Html image decoding is not supported'); } if (options.html && !options.inputFile) { throw new Error('No file to encode was provided'); } // jwt handling if (options.jwt) { const jwtParts = options.jwt.split('.'); if (jwtParts.length === 3) { console.log(chalk.blue('Header :', JSON.stringify(JSON.parse(Buffer.from(jwtParts[0], 'base64').toString("utf8")), null, 4))); console.log(chalk.green('Body :', JSON.stringify(JSON.parse(Buffer.from(jwtParts[1], 'base64').toString("utf8")), null, 4))); } else { console.log(chalk.red('invalid JWT token')); } exit(); } // html images handling if (options.html) { const imageContentB64 = fs.readFileSync(options.inputFile).toString('base64'); const mimeType = base64ImageMime.getImageMime(imageContentB64); const imageHtml = `<img src="data:${mimeType};base64,${handleWebSafe("ENCODE", imageContentB64)}" />`; if (options.outputFile) { // create recursive directories for output fs.mkdirSync(path.dirname(options.outputFile), { recursive: true }); fs.writeFileSync(options.outputFile, imageHtml); } else { console.log(chalk.yellow(imageHtml)); } exit(); } //handle stdin // if(options.encode === true && process.stdin) function handleWebSafe(mode, data) { if (options.webSafe) { if (mode === "ENCODE") { return data.replaceAll('+', '-').replaceAll('/', '_').replace(/=*$/, ''); } if (mode === "DECODE") { return data.replaceAll('-', '+').replaceAll('_', '/'); } } return data; } let inputEncoding = 'base64'; let inputAsB64string = ''; // we convert all inputs as base 64 to have common encoding, useful for exotic file conversions // handle "encode" input if (options.encode) { inputEncoding = 'utf8'; if (options.encode !== true) { inputAsB64string = Buffer.from(options.encode, inputEncoding).toString('base64'); } else if (options.inputFile) { inputAsB64string = fs.readFileSync(options.inputFile).toString('base64'); } // handle websafe characters inputAsB64string = handleWebSafe("ENCODE", inputAsB64string); } //handle "decode" input if (options.decode) { inputEncoding = 'base64'; if (options.decode !== true) { inputAsB64string = options.decode; } else if (options.inputFile) { inputAsB64string = fs.readFileSync(options.inputFile).toString('ascii'); } // handle websafe characters inputAsB64string = handleWebSafe("DECODE", inputAsB64string); } // handle output const outputBuffer = Buffer.from(inputAsB64string, inputEncoding); if (options.outputFile) { // create recursive directories for output fs.mkdirSync(path.dirname(options.outputFile), { recursive: true }); if (options.decode) { fs.writeFileSync(options.outputFile, Buffer.from(inputAsB64string, 'base64')); } else { fs.writeFileSync(options.outputFile, outputBuffer); } } else { console.log(chalk.yellow(outputBuffer.toString())); } } catch (e) { console.error(chalk.red(e)); exit(1); }