A map-based composable call and response framework. cmd-funk
is a great option for building lightweight command lines, as well as within more complicated applications that leverage a similar access pattern (like chat bots). Really, anything that predominantly communicates using nested string commands can leverage cmd-funk
.
- Composable: Every command line tool has different requirements. Insofar as it is useful,
cmd-funk
provides the tools to create your tools in whatever way you see fit. - Functional: To the extent that it's reasonable,
cmd-funk
doesn't explicitly track state. There's no classes, minimal initialization, as little overhead as possible. - Modern:
cmd-funk
is written in Typescript and takes advantage ofasync/await
, so no outdated access patterns!
Use your favorite package manager and install cmd-funk
, for example:
npm i cmd-funk
There are 3 building blocks to a cmd-funk
application, Mappers, Marshallers, and Outputs.
- Mappers are objects that map commands to their implementations (or other mappers!).
- Marshallers take the output of an implementation and convert it to something
cmd-funk
understands. - Outputs are special objects that store information about the result of a command.
Mappers are dictionaries that lead to the implementation logic of your commands, as well as the necessary help text associated with them.
Let's say you've got a simple CLI with three commands; add
, subtract
, and multiply
. With this architecture, you'd need just one mapper in the following shape:
import { SimpleMapper } from "cmd-funk";
export const MyCommandMap: SimpleMapper = {
add: async cmd => // ...do an add command
subtract: async cmd => // ...do a subtract command
multiply: async cmd => // ...do a multiply command
help: () => // ...return a helptext response
};
Let's look at what's going on...
async
functions are used here, but they are not required.cmd-funk
expects each key of a mapper object to return either an output or a promise to one.SimpleMapper
is a helper mapper implementation. Use this if you don't need custom contexts.- The
help
key is required on all mappers, and it implements the help text returned to your users.
Since the underlying implementation of a given command likely returns information specific to the applicastion, but each mapper key expects a return of an output or a promise to one, there is a need for an intermediary to translate between these two things.
This is where marshallers are helpful. Let's look at the example mapper from above, but add in an implementation for the add
command, which returns a string:
import { Marshallers, SimpleMapper } from "cmd-funk";
const add = async (cmd: SimpleCommand): Promise<string> => {
// ...some async logic
return "hello world";
}
export const MyCommandMap: SimpleMapper = {
add: async cmd => Marshallers.str(await add(cmd)),
subtract: async cmd => // ...do a subtract command
multiply: async cmd => // ...do a multiply command
help: () => // ...return a helptext response
};
So, what's going on?
Marshallers
is a provided namespace withincmd-funk
with implementations for some common marshaller use cases. They take in common return types and translate them tocmd-funk
outputs.SimpleCommand
is a helper implementation of the command type to use if you don't need custom contexts.
cmd-funk
ships with a few marshallers built in to get your project off the ground, they are as follows:
error
: for marshalling error messages, and optionally the command from which they spawned.help
: for marshalling help text. The suggested use case is passing in a key/value map of available commands within a mapper.keyValue
: for mapping an object consisting of key/value pairs (with an optional title) to formatted console output.str
: for mapping simple strings to console output.
cmd-funk
provides the CommandOutput<T>
type to simplify handling of command results (source). While use is not required, many helper functions leverage it, and all marshallers return objects of this type.
UNDER CONSTRUCTION
A Hello world CLI using cmd-funk
.