@sodacore/cli
Version:
Sodacore CLI is a plugin that offers CLI functionality within the framework.
126 lines (125 loc) • 4.52 kB
JavaScript
import { confirm, group, isCancel, log, multiselect, select, text } from '@clack/prompts';
export default class Commands {
constructor(exitHandler) {
this.exitHandler = exitHandler;
this.commands = {
'_:authenticate': this.handleAuthenticate.bind(this),
'_:error': this.handleError.bind(this),
'_:commands': this.handleCommands.bind(this),
'_:interact': this.handleInteract.bind(this),
'_:log': this.handleLog.bind(this),
'_:menu': this.handleMenu.bind(this),
'_:exit': this.handleExit.bind(this),
};
}
setSocket(socket) {
this.socket = socket;
}
async handle(command, context) {
// Check if the command exists.
if (!this.commands[command]) {
log.error(`Command ${command} not found.`);
return false;
}
// Check if the socket is authenticated.
if (!this.socket.data.authenticated && !command.startsWith('_:')) {
log.error('Socket is not authenticated.');
return false;
}
// Check if the command is valid.
const status = await this.commands[command](context);
if (status === false)
return false;
return true;
}
async handleAuthenticate(context) {
if (context.status) {
log.success('Connection was successfully authenticated.');
this.socket.data.authenticated = true;
this.write('_:commands');
}
else {
log.error('Connection was not authenticated, the connection will be closed.');
this.socket.data.authenticated = false;
}
return true;
}
async handleError(context) {
log.error(`Error: ${context.message}`);
return true;
}
async handleCommands(context) {
if (context.commands.length === 0) {
log.info('No commands available.');
return false;
}
// Define the command the user wants to run.
const commandToRun = await select({
message: 'What command do you want to run?',
options: context.commands.map(command => ({
value: command,
label: command,
})),
});
if (isCancel(commandToRun)) {
log.error('Operation cancelled, closing connection.');
this.exitHandler('Operation cancelled');
}
// Send the command to the server.
this.write(String(commandToRun));
return true;
}
async handleInteract(context) {
// Create the prompts group.
const prompts = context.commands.reduce((prompts, command) => {
if (command.type === 'text') {
prompts[command.key] = () => text(command.options);
}
else if (command.type === 'confirm') {
prompts[command.key] = () => confirm(command.options);
}
else if (command.type === 'select') {
prompts[command.key] = () => select(command.options);
}
else if (command.type === 'multiselect') {
prompts[command.key] = () => multiselect(command.options);
}
return prompts;
}, {});
// Request the results.
const results = await group(prompts, {
onCancel: () => {
log.error('Operation cancelled, will send null for this question.');
return true;
},
});
// Loop the results and check if the user cancelled the operation.
Object.keys(results).forEach(key => {
if (results[key] === 'canceled') {
results[key] = null;
}
});
// Send the results back to the server.
this.write('_:interact', { uid: context.uid, results });
// Return true.
return true;
}
async handleMenu(context) {
if (context?.message)
log.info(context?.message);
this.write('_:commands');
return true;
}
async handleLog(context) {
log[context.type](context.message);
return true;
}
async handleExit(context) {
log.error(`Connection closed: ${context.message}`);
this.exitHandler(context.message);
return true;
}
write(command, context = {}) {
this.socket.write(JSON.stringify({ _uid: this.socket.data.uid, command, context }));
}
}