
Research
Two Malicious Rust Crates Impersonate Popular Logger to Steal Wallet Keys
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
@thi.ng/args
Advanced tools
Declarative, functional CLI argument/options parser, app framework, arg value coercions, multi/sub-commands, usage generation, error handling etc.
[!NOTE] This is one of 210 standalone projects, maintained as part of the @thi.ng/umbrella monorepo and anti-framework.
🚀 Please help me to work full-time on these projects by sponsoring me on GitHub. Thank you! ❤️
Declarative, functional CLI argument/options parser, app framework, arg value coercions, multi/sub-commands, usage generation, error handling etc..
The parser includes built-in support for the following argument types (of course custom arg types are supported too):
Argument type | Multiple | Example | Result |
---|---|---|---|
Flag | --force , -f | force: true | |
String | ✅ | --foo bar | foo: "bar" |
Float/int/hex | ✅ | --bg ff997f | bg: 16750975 |
Enum | ✅ | --type png | type: "png" |
KV pairs | ✅ | --define foo=bar | define: { foo: "bar" } |
KV multi pairs | ✅ | -D foo=bar -D foo=baz | define: { foo: ["bar", "baz"] } |
JSON | --config '{"foo": [23]}' | config: { foo: [23] } | |
Fixed size tuple | --size 640x480 | size: { value: [640, 480] } |
If multiple values/repetitions are allowed for an argument, the values will be
collected into an array (apart from KV pairs, which will yield an object).
Furthermore, for multi-args and tuples, an optional delimiter can be specified
to extract individual values, e.g. -a 1,2,3
equals -a 1 -a 2 -a 3
The following commonly used arguments are available as predefined presets:
Higher order, configurable preset specs:
To use these presets, simply import and splice them into your own arg definitions (see code examples below).
The package provides a simple framework to conveniently define single and multi-command applications in a declarative and modular manner. Such apps are defined via command specs and other configuration options. The framework then handles all argument parsing, validation, usage display and delegation to sub-commands.
The wrapper defines a user-customizable command
context with all
important information which is passed to the commands and also includes a logger
(writing to stderr
). Other help/usage and error output also respects the
NO_COLOR
convention.
A fully documented code example is further below.
For some publicly available production uses, please see the related packages section in this readme.
STABLE - used in production
Search or submit any issues for this package
required: true
or
given a default
valueoneOf(["a","b","c"], {...})
oneOf({ opts: ["a","b","c"], ...})
tuple(identity, 3, {...})
tuple({ size: 3, coerce: identity, ...})
delim
iters are now to be included in the arg spec (rather
than given as separate function arg)yarn add @thi.ng/args
ESM import:
import * as args from "@thi.ng/args";
For Node.js REPL:
const args = await import("@thi.ng/args");
Package sizes (brotli'd, pre-treeshake): ESM: 3.47 KB
Note: @thi.ng/api is in most cases a type-only import (not used at runtime)
import {
ARG_VERBOSE,
flag,
hex,
json,
kvPairs,
oneOf,
parse,
size,
string,
vec,
type Args,
type KVDict,
type Tuple,
} from "@thi.ng/args";
type ImgType = "png" | "jpg" | "gif" | "tiff";
// CLI args will be validated against this interface
interface TestArgs {
configPath?: string;
force?: boolean;
bg: number;
type: ImgType;
size?: Tuple<number>;
pos?: Tuple<number>;
xtra?: { a: number; b: string };
define?: KVDict;
verbose: boolean;
}
// arg specifications
const specs: Args<TestArgs> = {
// re-use predefined preset (see readme section above)
...ARG_VERBOSE,
// string arg
configPath: string({
alias: "c",
hint: "PATH",
desc: "Config file path (CLI args always take precedence over those settings)",
}),
// boolean flag (default: false)
force: flag({
alias: "f",
desc: "Force operation",
// side effect and/or validation
// parsing only continues if function returns true
fn: (_) => (console.log("force mode enabled"), true),
}),
// hex int value
bg: hex({
desc: "Background color",
// mandatory args require a `default` value and/or `required: true`
default: 0xffffff,
defaultHint: "ffffff",
}),
// enum value (mandatory)
type: oneOf({
alias: "t",
desc: "Image type",
opts: ["png", "jpg", "gif", "tiff"],
// mandatory args require a `default` value and/or `required: true`
required: true,
}),
// fixed size numeric tuple w/ `x` as delimiter
size: size({ size: 2, hint: "WxH", desc: "Target size", delim: "x" }),
// syntax sugar for:
// size: tuple(2, coerceInt, { hint: "WxH", desc: "Target size" }, "x"),
// another version for tuples of floating point values
pos: vec({ size: 2, desc: "Lat/Lon coordinates", hint: "LAT,LON" }),
// syntax sugar for:
// pos: tuple(2, coerceFloat, { desc: "Lat/Lon" }),
// JSON string arg
xtra: json({
alias: "x",
desc: "Extra options",
group: "extra",
}),
// key-value pairs parsed into an object (multiple allowed)
define: kvPairs({
alias: "D",
desc: "Define dict entry",
group: "extra"
}),
};
try {
// parse argv w/ above argument specs & default options
// (by default usage is shown if error occurs)
const args = parse(specs, process.argv, {
usageOpts: {
prefix: `
█ █ █ │
██ █ │
█ █ █ █ █ █ █ █ │ @thi.ng/args demo app
█ █ █ █ █ █ █ █ █ │ v1.0.0
█ │
█ █ │\n\n`,
showGroupNames: true,
groups: ["flags", "main", "extra"],
lineWidth: 72,
},
});
console.log(args);
} catch (_) {}
Invoking this as CLI script without arguments will generate an error about a
missing --type
arg and output the generated usage info (by default with ANSI
color highlights):
illegal argument(s): missing arg: --type
█ █ █ │
██ █ │
█ █ █ █ █ █ █ █ │ @thi.ng/args demo app
█ █ █ █ █ █ █ █ █ │ v1.0.0
█ │
█ █ │
Flags:
-f, --force Force operation
-v, --verbose Display extra information
Main:
--bg HEX Background color (default: "ffffff")
-c PATH, --config-path PATH Config file path (CLI args always take
precedence over those settings)
--pos N,N Lat/Lon coordinates
--size WxH Target size
-t ID, --type ID [required] Image type: "png", "jpg",
"gif", "tiff"
Extra:
-D key=val, --define key=val [multiple] Define dict entry
-x JSON, --xtra JSON Extra options
Usage information can be generated via usage()
and is automatically triggered
via the special --help
option (configurable, see
ParseOpts).
Each arg can be associated with arbitrary group IDs, which are then used to
segment usage output. By default, flag()
args are assigned to a "flags"
group, all others to "main"
. The default output order too is ["flags", "main"]
, but can be configured via a group
option given an arg spec or
factory function.
By default, ANSI colors are used to format the result string of usage()
, but
can be disabled (see
UsageOpts
).
The below invocation demonstrates how the various argument types are handled &
represented in the result. Parsing stops with the first non-argument value (here
sourcefile.png
) and the remaining values are made available via rest
in the
result object.
bun index.ts \
-f -t png --bg ff00ff --size 640x480 \
-D author=toxi -D date=2018-03-24 \
--xtra '{"foo": [23]}' \
sourcefile.png
# force mode enabled
# {
# result: {
# force: true,
# type: 'png',
# bg: 16711935,
# size: Tuple { value: [640, 480] }
# define: { author: 'toxi', date: '2018-03-24' },
# xtra: { foo: [23] },
# verbose: false,
# },
# index: 15,
# rest: [ 'sourcefile.png' ],
# done: false
# }
The following example defines a CLI app with two sub-commands: hello
and
list
. Each command has its own options, in addition to common/shared ones.
Each command is defined in a modular manner (usually in its own source file).
All aspects like arg parsing, validation, and command selection/delegation is
handled by the cliApp()
wrapper.
import {
ARG_VERBOSE,
cliApp,
configureLogLevel,
int,
string,
type Command,
type CommandCtx,
} from "@thi.ng/args";
import { files } from "@thi.ng/file-io";
// common command opts
interface CommonOpts {
verbose: boolean;
}
// custom command context
interface AppCtx<T extends CommonOpts> extends CommandCtx<T, CommonOpts> {
// plus any custom additions here...
}
// command-specific options
interface HelloOpts extends CommonOpts {
name: string;
}
// command definition
const HELLO: Command<HelloOpts, CommonOpts> = {
// brief description (for `--help` usage)
desc: "Print out a greeting",
// command specific options (arguments)
// (will be combined with common opts)
opts: {
name: string({
alias: "n",
desc: "Name for greeting",
required: true,
}),
},
// this command does not accept any inputs
inputs: 0,
// command implementation
fn: async (ctx) => {
// log message only shown if `--verbose`/`-v` given
ctx.logger.debug("opts", ctx.opts);
console.log(`Hello, ${ctx.opts.name}!`);
},
};
// command-specific options
interface ListFilesOpts extends CommonOpts {
depth: number;
filter?: string;
}
// command definition
const LIST_FILES: Command<ListFilesOpts, CommonOpts> = {
// brief description (for `--help` usage)
desc: "List files in given dir",
// command specific options
opts: {
filter: string({
alias: "f",
desc: "Filter regexp",
}),
depth: int({
alias: "d",
desc: "Recursion depth (directory levels)",
default: Infinity,
}),
},
// this command requires exactly 1 input
// (if supporting a range, use `[min, max]`)
inputs: 1,
// command implementation
fn: async (ctx) => {
for (let f of files(ctx.inputs[0], ctx.opts.filter, ctx.opts.depth)) {
console.log(f);
}
},
};
// define & start CLI app
cliApp<CommonOpts, AppCtx<any>>({
// app name
name: "example",
// process.argv index from which to start parsing from
start: 2,
// list common command opts here
opts: {
// re-use verbose flag arg spec preset
...ARG_VERBOSE,
},
// list of commands
commands: {
hello: HELLO,
list: LIST_FILES,
},
// set to true if only a single command
// in this case the command name would NOT be required/expected
// single: true,
// usage opts
usage: {
// prefix/header string
prefix: `Example app
===================================
Usage: example [opts] [inputs]\n`,
// configure column width for param usage info
paramWidth: 24,
lineWidth: 80,
},
// context initialization/augmentation
// (called before arg parsing commences)
ctx: async (ctx) => {
configureLogLevel(ctx.logger, ctx.opts.verbose);
return ctx;
},
});
Example usage (here using bun
to launch the above CLI app, though the usage
info is written to assume an example
launcher/wrapper):
bun readme-cliapp.ts
# Example app
# ===================================
# Usage: example [opts] [inputs]
#
# Available commands:
#
# hello : Print out a greeting
# list : List files in given dir
#
# -v, --verbose Display extra information
# displaying help for a sub-command
bun readme-cliapp.ts hello --help
# Example app
# ===================================
# Usage: example [opts] [inputs]
#
# Current command:
#
# hello : Print out a greeting
#
# -v, --verbose Display extra information
#
# -n STR, --name STR [required] Name for greeting
# invoking `hello` sub-command (with verbose flag)
bun readme-cliapp.ts hello --name thi.ng -v
# [DEBUG] example: opts {"name":"thi.ng","verbose":true}
# Hello, thi.ng!
# invoking `list` sub-command
bun readme-cliapp.ts list -d 2 -f '.js' .
# ./dev/api.js
# ./dev/runtime.js
# ./dev/test/main.js
# ./index.js
# missing arg error
bun readme-cliapp.ts hello
# illegal argument(s): missing arg: --name
#
# (...additional usage output omitted for brevity)
If this project contributes to an academic publication, please cite it as:
@misc{thing-args,
title = "@thi.ng/args",
author = "Karsten Schmidt",
note = "https://thi.ng/args",
year = 2018
}
© 2018 - 2025 Karsten Schmidt // Apache License 2.0
FAQs
Declarative, functional CLI argument/options parser, app framework, arg value coercions, multi/sub-commands, usage generation, error handling etc.
We found that @thi.ng/args demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
Research
A malicious package uses a QR code as steganography in an innovative technique.
Research
/Security News
Socket identified 80 fake candidates targeting engineering roles, including suspected North Korean operators, exposing the new reality of hiring as a security function.