@j-cake/jcake-utils
v1.2.1-r0
Published
A collection of utilities for everyday nodejs usage.
Downloads
1
Readme
JCake-Utils
As I write more stuff, I accumulate different utilities that I use in my projects.
Instead of copy-pasting them into each project I intend to use them in, I'll just make a repo containing each of them.
The idea is that if you need to use one, you just add the repo as a dependency to your package.json
, and call import
like this:
import DB from 'jcake-utils/db';
import args from 'jcake-utils/args';
import buffer from 'jcake-utils/buffer';
...
Utils
- [DB](#db)
- [Arg Parser](#args)
- [Iter/AsyncIter](#iters)
- [Buffer](#buffer)
- [String Utilities](#strings)
db
JCake Database Objects are database files with a specialised binary format, analogous to a filesystem - containing data in tree-like structures. This allows a user to query the database and access a customised set of data. As of the present, the database cannot manipulate the result of a query, but can only access the data. To do this, the user is expected to be able to perform data processing after the query has completed.
The database interface works as follows:
- Open a file with Read/Write permissions (Write is optional, but recommended) through the NodeJS
fs.promises
interface.- For read-only access to the database, the
readonly
parameter should be passed to the constructor for clarity.
- For read-only access to the database, the
- The database constructor accepts an
fs.promises.FileHandle
object as its first argument. - For TypeScript users, it's a very good idea to define the type of data contained in teh database, by passing an interface as its first and only generic argument.
- Database queries are an array of type
keyof any
. The query system is fully typed and will only accept a list of keys that are defined in the provided interface. To disable this behaviour, passany
as the first generic argument. - For a command-line interface, the database can be queried through the
jcake-utils/dbrepl
module.
args
Parsing CLI arguments is a pain, especially without a framework. However, most provide amazing functionality, making them very bloated.
JCake-utils
provides a very basic and fully typed CLI argument parser.
To use it, simply import it and call the parse
function with the arguments you want to parse.
It accepts two arguments, the first is a map of options, while the second is the default argument, which in most CLI applications is the first argument without an accompanying argument name, such as a double-dash argument.
An example:
import parse, * as Format from 'jcake-utils/args';
export const config = parse({
port: {
long: 'port',
short: 'p',
format: Format.Int,
default: 8080
},
database: {
long: 'database',
short: 'd',
format: Format.Path(true),
},
silent: {
long: 'silent',
short: 's',
}
}, Format.Path(true))(process.argv.slice(2));
If the above program is called through node program.js --port=8080 --database=./db.bin --silent
, the output will be:
{
port: 8080,
database: './db.bin',
silent: true
}
Notice how omitting the format
property will force the parser to treat the argument as a boolean switch.
It is worth emphasising that the parser is fully typed. So typeof config
will be {port: number, database: string, silent: boolean, default: string}
.
It is also possible to define custom formatters. A custom formatter is any function which accepts a token of type string
and returns any
. By specifying types, the parser will correctly return the return type in the result.
An example would be of date formatting.
import parse, * as Format from 'jcake-utils/args';
export const config = parse({
date: {
long: 'date',
short: 'd',
format: tok => new Date(tok),
}
})(process.argv.slice(2));
typeof config = {
date: Date,
default: undefined
}
Omitting the default
property will cause the parser to throw an error if the argument is not provided.
iters
Iterators are a very useful tool for handling potentially large sets of data with a small memory footprint. However, unline languages like Rust, there is no standard interface around them. Of course you can use for
loops, but the functionality of general manipulation is lacking. Hence the Iter
/IterSync
functions act like constructors which provide chainable functions for manipulating the data.
They provide functionality to map
, filter
, filtermap
, reduce
, concat
and collect
, and can be passed to a for
at each point, without needing to be collect
ed.
AsyncIterators
AsyncIterators expose additional functionality which is nonsensical for synchronous iterators. They are
interleave
- Take a list of asyncIterators and interleave their results.await
- Flatten nestedPromise
s in an asyncIterator, allowing you to useasync
functions in themap
for instance.
buffer
NodeJS Buffers provide windows into a region of contiguous memory. They are useful for manipulating raw bytes directly. However lack functionality for prepopulating them with data. The buffer
module provides a number of functions for instantiating buffers.
The buffer
module is presently optimised for use in previous projects, so only includes unsigned integer in Little Endian format, but proves useful regardless.
Each method is chainable, until the done
function is called, at which point a completed Buffer
object is returned containing the sequence of bytes inserted previously.
strings
These are general-purpose string utilities - things I tend to find myself repeating.
- Bracket counting
- Friendly file sizes
- Custom whitespace trimmers
- Prefixing lines
etc.