๐ HBH-Nodes
A modern, dependency-light Node.js utility toolkit designed for developers who love clarity, control, and composability. Every module is intentional, transparent, and production-ready.
๐ฆ What is HBH-Nodes?
HBH-Nodes is not a framework. It is a collection of carefully crafted utilities that solve common Node.js problems:
- ๐๏ธ Observing object changes (deep & reactive)
- โ๏ธ Controlling async concurrency
- ๐ Safe JSON & package.json handling
- ๐งฐ CLI bootstrapping
- ๐งผ Sanitization
- ๐ง Function safety & control
- ๐ Auto documentation generation
Each utility is standalone, meaning you only use what you need.
๐ Installation
1npm install hbh-nodes
1import * as HBH from 'hbh-nodes';
Or selective imports:
1import { chokidar, ConcurrencyQueue } from 'hbh-nodes';
๐ง Core Philosophy
โจ Simple > Clever ๐งฉ Composable > Opinionated ๐ Explicit > Magical
HBH-Nodes is ideal for:
- CLI tools
- Internal dev tooling
- Backend utilities
- Scripts that grow into systems
๐ค AlphabetNumberConverter
AlphabetNumberConverter is a utility that converts numbers, strings, and JavaScript objects into compact encoded strings using a configurable alphabet. All conversions are reversible and support BigInt for large values.
Installation
1npm install hbh-nodes
Import
1import { AlphabetNumberConverter } from "hbh-nodes";
(CommonJS)
1const { AlphabetNumberConverter } = require("hbh-nodes");
Encode and Decode Numbers
1const conv = new AlphabetNumberConverter();3const code = conv.encode(12345);4console.log(code); 5// Example output: "dnh"7console.log(conv.decode(code)); 8// Example output: 12345n
Encode and Decode Strings
1const encoded = conv.encodeString("hello@example.com");2console.log(encoded); 3// Example output: "gyTfA...something"5const decoded = conv.decodeToString(encoded);6console.log(decoded); 7// Output: "hello@example.com"
Encode and Decode Objects
1const user = { id: 1, name: "Alice" };3const code = conv.encodeObject(user);4console.log(code);5// Example output: "bHdKfL..."7const obj = conv.decodeObject(code);8console.log(obj);9// Output: { id: 1, name: "Alice" }
Auto-Type Encode (number, string, object)
1console.log(conv.encode(42));2// Example: "gG"4console.log(conv.encode("hello"));5// Example: "dfKsL..."7console.log(conv.encode({ a: 1 }));8// Example: "kLf92..."
Custom Alphabet
1const custom = new AlphabetNumberConverter({2 chars: "0123456789abcdef",3 base: 164});6const code = custom.encode(255);7console.log(code);8// Example output: "ff"10console.log(custom.decode(code));11// Output: 255n
Count Possible Codes
1console.log(conv.uniqueCodesCount(52, 5)); 2// Example output: 380204032n (52^5)
Quick Example
1const conv = new AlphabetNumberConverter();3console.log(conv.encode(2025)); 4// Example: "kH"6console.log(conv.decode("kH")); 7// Output: 2025n9console.log(conv.encode("chatgpt"));10// Example: "fL2kA..."12console.log(conv.encode({ ok: true }));13// Example: "bKf92..."
๐งฎ applyCalc
applyCalc is a powerful utility for safely manipulating values, including numbers, strings, arrays, and objects. It supports advanced operations such as mathematical expressions, date adjustments, string transformations, and recursive object merging, all while providing safeguards against unsafe code execution.
Import
1import { applyCalc } from "hbh-nodes";
All functions from this module are available under the applyCalc namespace.
Functions
1. applyCalc.applyCalc(oldVal, newVal, context = {})
Applies transformations to a value (oldVal) based on the input (newVal). Supports:
- Numbers: arithmetic, percentage changes, math expressions, increments/decrements.
- Dates: adding/subtracting days, months, years, hours, minutes, or seconds.
- Strings: modifiers like trim, upper/lowercase, capitalize, title case, reverse, append, prepend, slice, padding, repeat, and regex replacement.
- Objects & Arrays: recursive updates, deletion, merging, pushing/popping/shifting/unshifting array items, deduplication, and conditional fallback.
Example:
1const oldVal = 10;2const newVal = "++5"; // increment by 53const result = applyCalc.applyCalc(oldVal, newVal);4console.log(result); // 15
1const date = new Date("2025-01-01");2const newDate = applyCalc.applyCalc(date, "++10d");3console.log(newDate); // 2025-01-11
1const str = "hello";2const modified = applyCalc.applyCalc(str, " | upper | reverse");3console.log(modified); // "OLLEH"
1const obj = { a: 1, b: 2 };2const updated = applyCalc.applyCalc(obj, { b: "__DELETE__", c: 3 });3console.log(updated); // { a: 1, c: 3 }
2. applyCalc.safeMathEval(expr, val, context = {})
Safely evaluates mathematical expressions using only allowed operations. Prevents access to global objects, constructors, or dangerous code.
Example:
1const result = applyCalc.safeMathEval("val * 2 + 5", 10);2console.log(result); // 25
3. applyCalc.interpolateString(str, context)
Replaces placeholders in a string with values from a context object. Placeholders use {{key}} syntax.
Example:
1const text = "Hello {{name}}!";2const result = applyCalc.interpolateString(text, { name: "Alice" });3console.log(result); // "Hello Alice!"
4. applyCalc.applyStringModifiers(val, modifiers, context)
Applies a series of string transformations/modifiers to a value. Supports:
trim,upper,lower,capitalize,title,reverseappend:<text>,prepend:<text>slice:start:end,padStart:length:char,padEnd:length:char,repeat:countreplace:from:to,replaceRegex:/pattern/flags:replacement- Interpolation using
{{key}} - Special:
__now__โ current ISO timestamp
Example:
1const str = " hello world ";2const result = applyCalc.applyStringModifiers(str, ["trim", "title"]);3console.log(result); // "Hello World"
5. applyCalc.dateAdd(date, amount, unit)
Adds or subtracts units of time from a JavaScript Date object. Supported units: d (days), m (months), y (years), h (hours), min (minutes), s (seconds).
Example:
1const newDate = applyCalc.dateAdd(new Date("2025-01-01"), 5, "d");2console.log(newDate); // 2025-01-06
Usage Example
1import { applyCalc } from "hbh-nodes";3// Number increment4const num = applyCalc.applyCalc(100, "++10%");5console.log(num); // 1107// String transformation8const text = applyCalc.applyCalc("hello", "| upper | reverse");9console.log(text); // "OLLEH"11// Object update with deletion12const obj = { a: 1, b: 2 };13const updated = applyCalc.applyCalc(obj, { b: "__DELETE__", c: 3 });14console.log(updated); // { a: 1, c: 3 }16// Array merge and deduplicate17const arr = [{ id: 1 }, { id: 2 }];18const merged = applyCalc.applyCalc(arr, { __MERGE__: true, items: [{ id: 2 }, { id: 3 }], __UNIQUE__: true });19console.log(merged); // [{ id: 1 }, { id: 2 }, { id: 3 }]
๐ chokidar
The chokidar function provides a reactive object watcher. It wraps any JavaScript object, array, Map, or Set with a Proxy and emits events whenever the data changes. This allows you to listen to changes like additions, updates, deletions, or structural mutations.
Import
1import { chokidar } from "hbh-nodes";
Function
chokidar(obj, path = '')
Wraps an object and returns a proxied version that emits events on changes. Supports:
- Plain objects
- Arrays
- Maps
- Sets
Parameters:
objโ The object, array, Map, or Set to watch.pathโ Optional string indicating the root path (useful for nested structures).
Returns:
A proxied version of obj that emits events for any changes.
Events
The returned proxied object supports .on(event, callback) using an internal EventEmitter.
Supported Events
| Event | When It Fires | Arguments |
|---|---|---|
add |
A new property, array element, Map key, or Set value is added | (path, oldValue, newValue) |
update |
An existing property, array element, or Map key is updated | (path, oldValue, newValue) |
delete |
A property, array element, Map key, or Set value is removed | (path, oldValue) |
clear |
Map.clear() or Set.clear() is called |
(path, oldSnapshot) |
Notes:
- For nested objects,
pathautomatically represents the full path (e.g.,user.address.city). - Arrays emit
updateevents for structural methods (push,pop,shift,unshift,splice,sort,reverse) with the updated array state. - Maps emit events for
set,delete, andclear. - Sets emit events for
add,delete, andclear.
Example Usage
1import { chokidar } from "hbh-nodes";3const data = {4 user: { name: "Alice", age: 25 },5 items: [1, 2, 3],6 settings: new Map([['theme', 'dark']])7};9const watched = chokidar(data);11// Listen to updates12watched.user.on('update', (path, oldVal, newVal) => {13 console.log(`Updated ${path}:`, oldVal, 'โ', newVal);14});16watched.user.name = "Bob"; 17// Console: Updated user.name: Alice โ Bob19// Listen to array changes20watched.items.on('update', (path, oldVal, newVal) => {21 console.log(`Array updated at ${path}:`, oldVal, 'โ', newVal);22});24watched.items.push(4); 25// Console: Array updated at items: [1,2,3] โ [1,2,3,4]27// Listen to Map changes28watched.settings.on('add', (path, _, newVal) => {29 console.log(`Added to ${path}:`, newVal);30});32watched.settings.set('language', 'en'); 33// Console: Added to settings.get(language): en
Features
- Deep Observation: Nested objects, arrays, Maps, and Sets are automatically wrapped with proxies.
- Full Event Coverage: Supports add, update, delete, and clear operations for all types.
- Path Tracking: Full paths are emitted for changes, even in deeply nested structures.
- Array Method Support: Emits events for mutating methods (
push,pop, etc.). - Map/Set Support: Monitors additions, deletions, and clearing operations.
Notes
- This implementation uses
Proxyobjects; thus, it requires modern JavaScript environments. - Event listeners are attached via the
.on()method. - Useful for reactive data structures, state management, and change tracking in complex objects.
๐ฆ ConcurrencyQueue
The ConcurrencyQueue class provides a simple concurrency limiter for asynchronous functions. It ensures that only a specified number of async operations run simultaneously, queuing the rest.
Import
1import { ConcurrencyQueue } from "hbh-nodes";
Class: ConcurrencyQueue
new ConcurrencyQueue(limit = 5)
Creates a new concurrency queue.
Parameters:
limitโ Maximum number of concurrent async operations. Default is5.
Properties:
limitโ Maximum concurrency limit.activeCountโ Current number of running operations.queueโ Internal queue of waiting promises.
Method: run(fn)
Executes an asynchronous function within the concurrency queue.
Parameters:
fnโ An async function (or function returning a promise) to execute.
Returns:
A promise resolving to the result of fn.
Behavior:
- If the number of currently running operations is below
limit,fnexecutes immediately. - If the limit is reached, the function waits in a queue until a slot is available.
- After
fnfinishes, the next function in the queue starts automatically.
Example Usage
1import { ConcurrencyQueue } from "hbh-nodes";3const queue = new ConcurrencyQueue(2);5async function task(id, delay) {6 console.log(`Task ${id} started`);7 await new Promise(r => setTimeout(r, delay));8 console.log(`Task ${id} finished`);9 return id;10}12// Run multiple tasks concurrently but limited to 2 at a time13const results = await Promise.all([14 queue.run(() => task(1, 1000)),15 queue.run(() => task(2, 500)),16 queue.run(() => task(3, 700)),17 queue.run(() => task(4, 300))18]);20console.log(results); // [1, 2, 3, 4]
Features
- Concurrency Limit: Only
limitasync operations run simultaneously. - Queueing: Additional calls wait in a FIFO queue.
- Automatic Execution: As soon as one task finishes, the next in queue starts automatically.
- Simple API:
run(fn)is all thatโs needed to schedule tasks.
Notes
- Useful for limiting network requests, API calls, file operations, or any asynchronous workloads that must be throttled.
- Supports any async function, including ones returning promises.
- If no limit is specified, defaults to
5.
๐ convertObject
The convertObject function provides a flexible way to transform, filter, and traverse objects. It supports multiple output formats, deep flattening, filtering, mapping, and sorting.
Import
1import { convertObject } from "hbh-nodes";
Function: convertObject(obj, options = {})
Converts or extracts data from an object according to specified rules.
Parameters:
objโ The object to transform.optionsโ Optional settings:
| Option | Type | Default | Description | ||
|---|---|---|---|---|---|
outputFormat |
`'flat' | 'tree' | 'nestedKeys'` | 'flat' |
Output structure format. |
props |
string | string[] | RegExp |
null |
Filter keys to include. | ||
filterFn |
(val, key, path) => boolean |
null |
Custom filter function. | ||
mapFn |
(val, key, path) => any |
(v) => v |
Transform values. | ||
includeKeys |
boolean |
false |
If true, flat output returns [key, value]. |
||
includePaths |
boolean |
false |
Use full path as key in flat output. | ||
flatten |
boolean |
true |
Traverse nested objects. | ||
defaultValue |
any |
undefined |
Fallback value for missing entries. | ||
strict |
boolean |
false |
Throw error if key/value not found. | ||
maxDepth |
number |
Infinity |
Limit traversal depth. | ||
sort |
boolean |
false |
Sort keys alphabetically. | ||
prefix |
string |
'' |
Start traversal at nested path. | ||
keyKey |
string |
'key' |
Key name in tree nodes. | ||
valueKey |
string |
'value' |
Value name in tree nodes. | ||
childrenKey |
string |
'children' |
Child array key for tree output. |
Output Formats
flatโ Returns a flat array of values (or[key, value]pairs).treeโ Returns a nested tree of nodes:{ key, value, children }.nestedKeysโ Returns nested keys only.simpleโ Returns direct keys or mapped values of the object.
Additional helper walkers are included for specialized tasks:
keysOnlyโ Extract only keys.valuesOnlyโ Extract only values.pathsOnlyโ Extract full paths.groupByTypeโ Group keys by value type.countValuesโ Count occurrences of values/arrays.filterAndMapโ Apply filter and map recursively.
Examples
Flat output with mapping
1import { convertObject } from "hbh-nodes";3const data = {4 user: { name: "Alice", age: 25 },5 settings: { theme: "dark" }6};8const result = convertObject(data, {9 outputFormat: 'flat',10 mapFn: (v, k) => String(v).toUpperCase()11});13console.log(result);14// ["ALICE", "25", "DARK"]
Tree output with filtering
1const tree = convertObject(data, {2 outputFormat: 'tree',3 filterFn: (val, key) => typeof val === 'string'4});6console.log(tree);7// [8// { key: 'user', children: [{ key: 'name', value: 'Alice' }] },9// { key: 'settings', children: [{ key: 'theme', value: 'dark' }] }10// ]
Nested keys output
1const keys = convertObject(data, { outputFormat: 'nestedKeys' });2console.log(keys);3// [["user", ["name", "age"]], ["settings", ["theme"]]]
Features
- Supports deep traversal and flattening.
- Custom filters via
propsorfilterFn. - Value transformations using
mapFn. - Flexible output formats (
flat,tree,nestedKeys). - Optional key/value inclusion, sorting, and strict mode.
- Traversal can start from a nested path using
prefix.
๐ generateDocs
Generates documentation for an API map. It analyzes each function in the provided map and extracts a description. This can be used to automatically build your docs site or README.
| Parameter | Type | Default | Description |
|---|---|---|---|
apiMap |
object |
โ | An object where keys are function names and values are the corresponding function references. Each function may optionally include a description property or a JSDoc comment. |
string โ A formatted string listing each function with its description.
-
Check
descriptionproperty: If a function has adescriptionproperty, that is used first. -
Check JSDoc-style comment: If no
descriptionproperty exists, it tries to extract a JSDoc comment from the function string. -
Fallback: If neither exists, it returns
"No description available."for that function.
1function add(a, b) {2 /** Adds two numbers together and returns the result */3 return a + b;4}6function multiply(a, b) {7 return a * b;8}9multiply.description = "Multiplies two numbers.";11const apiMap = { add, multiply };13console.log(generateDocs(apiMap));
Output:
- `add`: Adds two numbers together and returns the result
- `multiply`: Multiplies two numbers.
- Function without description or JSDoc:
1function noop() {}2const apiMap = { noop };3console.log(generateDocs(apiMap));
Output:
- `noop`: No description available.
- Function with multiline JSDoc:
1function complex(a, b) {2 /**3 * Performs a complex operation.4 * Returns a transformed value.5 */6 return a + b;7}8const apiMap = { complex };9console.log(generateDocs(apiMap));
Output:
- `complex`: Performs a complex operation. Returns a transformed value.
- Function with leading
*in JSDoc:
1function example() {2 /**3 * Example function4 * with multiple lines5 */6 return true;7}8const apiMap = { example };9console.log(generateDocs(apiMap));
Output:
- `example`: Example function with multiple lines
- Non-function values in map โ will skip properly because
.toString()works on anything, but best practice is to provide only functions.
- The function works best when the
apiMaponly contains functions. Non-function values may produce unexpected results. - Leading/trailing whitespace in JSDoc is automatically trimmed.
- Multi-line JSDoc comments are flattened into a single line for readability.
๐ฃ EventEmitter
A lightweight event emitter class that supports standard event handling patterns including on, once, off, and emit. Ideal for creating reactive systems or handling custom events in JavaScript.
Import
1import { EventEmitter } from "hbh-nodes";
Methods
on(eventName, listener)
Registers a listener for a given event. Multiple listeners can be registered for the same event.
Parameters:
eventName(string) โ Name of the event to listen to.listener(function) โ Callback function to execute when the event is emitted.
Returns:
The EventEmitter instance (supports chaining).
Example:
1const emitter = new EventEmitter();3emitter.on("data", (msg) => {4 console.log("Received:", msg);5});7emitter.emit("data", "Hello World"); // Logs: Received: Hello World
Edge Cases:
- Multiple listeners can be attached to the same event; all will be called in order of registration.
- Chaining allows multiple
oncalls in sequence:emitter.on("a", fn1).on("b", fn2).
once(eventName, listener)
Registers a one-time listener that will be automatically removed after the first invocation.
Parameters:
eventName(string) โ Event name.listener(function) โ Callback function.
Returns:
The EventEmitter instance (supports chaining).
Example:
1emitter.once("connect", () => console.log("Connected!"));3emitter.emit("connect"); // Logs: Connected!4emitter.emit("connect"); // No output
Edge Cases:
- The listener is removed immediately after execution.
- Safe to attach multiple
oncelisteners to the same event.
emit(eventName, ...args)
Triggers an event, calling all registered listeners with the provided arguments.
Parameters:
eventName(string) โ Event to trigger....argsโ Any number of arguments to pass to listeners.
Returns:
The EventEmitter instance (supports chaining).
Example:
1emitter.on("update", (oldVal, newVal) => {2 console.log(`Changed from ${oldVal} to ${newVal}`);3});5emitter.emit("update", 1, 2); // Logs: Changed from 1 to 2
Edge Cases:
- If no listeners exist for the event,
emitdoes nothing. - Errors in one listener do not prevent other listeners from executing; they are logged to console.
off(eventName, listener)
Removes a specific listener for an event. If no listener is provided, all listeners for that event are removed.
Parameters:
eventName(string) โ Event name.listener(function, optional) โ Specific listener to remove.
Returns:
The EventEmitter instance (supports chaining).
Example:
1const fn = (msg) => console.log(msg);2emitter.on("data", fn);3emitter.off("data", fn); // Removes fn
Edge Cases:
- Calling
offwith no listener removes all listeners for the event. - Safe to call
offon an event that has no listeners; does nothing.
removeAll(eventName)
Utility method to remove all listeners for a specific event.
Parameters:
eventName(string) โ Event name.
Returns:
The EventEmitter instance (supports chaining).
Example:
1emitter.on("x", () => {});2emitter.removeAll("x"); // All listeners for "x" removed
listeners(eventName)
Returns an array of all registered listeners for an event.
Parameters:
eventName(string) โ Event name.
Returns: Array of functions.
Example:
1emitter.on("msg", () => {});2console.log(emitter.listeners("msg").length); // 1
Edge Cases:
- Returns an empty array if no listeners are registered.
๐ FunctionWrapper
FunctionWrapper is a highly versatile utility class for JavaScript/TypeScript functions that allows you to enhance, monitor, transform, and control functions with a rich set of composable decorators. It extends a Logger base class to provide optional logging capabilities.
With FunctionWrapper, you can chain multiple function transformations, handle retries, measure performance, debounce or throttle calls, enforce validations, create undoable operations, and much moreโall in a highly modular and chainable manner.
Features
FunctionWrapper provides over 80+ static methods to extend and wrap functions. Some of the core capabilities include:
-
Logging and Monitoring
log(fn): Logs function calls and results.time(fn): Measures execution duration.profile(fn): Logs execution stats with timestamp.stats(fn): Tracks call count and average execution time.
-
Control Flow Enhancements
retry(fn, retries, delayMs): Automatically retries a function on failure.once(fn),oncePerArgs(fn): Execute only once globally or per argument set.after(fn, n),before(fn, n): Control execution after or before a number of calls.limit(fn, max): Limits total number of calls.lock(fn): Prevents concurrent execution.
-
Asynchronous Handling
debounce(fn, wait): Delays execution until idle.throttle(fn, wait): Limits execution frequency.delayFn(fn, delay),delayResult(fn, ms),delayEach(fn, ms): Flexible delayed execution.cancelable(fn): Creates cancelable promises.queue(fn): Serializes asynchronous calls.
-
Data Manipulation
memo(fn): Caches results for identical arguments.transformOutput(fn, transformer): Post-process function results.mask(fn, masker): Preprocess arguments before calling the function.randomizeArgs(fn): Shuffles arguments randomly.randomBehavior(fn, behaviors): Introduces probabilistic behaviors.
-
Validation and Safety
validate(fn, validator): Validates arguments.ensure(fn, validator): Validates output.catch(fn, onError, fallback): Graceful error handling.sandbox(fn, timeout): Safe execution with error handling and time limit.safeJson(fn): Safe JSON parsing and handling.
-
Functional Utilities
chain(fn): Chain multiple transformations in a fluent interface.pipe(...fns): Compose functions in a pipeline.tap(fn, tapFn): Tap into function results without affecting output.hook(fn, { before, after }): Run pre- and post-execution hooks.feedback(fn, logger): Collect input/output logs.
-
Simulation and Testing
simulate(fn, options): Simulate failures, delays, and data corruption.test(fn, testCases): Run predefined tests on a function.predict(fn, modelFn): Attach predictive models to results.
-
Batching and History
batch(fn, chunkSize, cb): Execute functions in batches.history(fn): Track inputs and outputs withgetHistoryandclearHistory.watch(fn): Subscribe to function call results.changelog(fn): Track changes in object outputs.
-
Advanced Features
undoable(fn, inverseFn): Support undo operations.locale(fn, formatter): Apply locale-based input/output formatting.smartIdle(fn, options): Execute during idle periods with cancellation.eventual(fn, checkFn, interval): Poll until a condition is met.timeLimit(fn, timeout): Force a maximum execution duration.
Installation
1npm install hbh-nodes
Or using yarn:
1yarn add hbh-nodes
Import
1import { FunctionWrapper as FW } from "hbh-nodes";2const { FunctionWrapper } = FW;
Usage
1. Basic Function Wrapping
1const add = (a, b) => a + b;2const loggedAdd = FunctionWrapper.log(add);4console.log(loggedAdd(2, 3));5// Logs: Calling add [2, 3]6// Logs: Result from add 5
2. Chaining Transformations
1const multiply = (a, b) => a * b;3const enhancedFn = FunctionWrapper.chain(multiply)4 .log()5 .time()6 .memo()7 .value();9console.log(enhancedFn(5, 6));
3. Debounce / Throttle
1const save = (data) => console.log('Saved:', data);2const debouncedSave = FunctionWrapper.debounce(save, 500);3const throttledSave = FunctionWrapper.throttle(save, 1000);
4. Retry with Delay
1const unstable = () => {2 if (Math.random() < 0.7) throw new Error('Fail');3 return 'Success';4};6const reliable = FunctionWrapper.retry(unstable, 5, 100);7reliable().then(console.log).catch(console.error);
5. Undoable Functions
1let counter = 0;2const increment = () => ++counter;3const decrement = () => --counter;5const undoableIncrement = FunctionWrapper.undoable(increment, decrement);6undoableIncrement(); // counter = 17undoableIncrement.undo(); // counter = 0
6. Monitoring and Stats
1const slowFn = (x) => new Promise(res => setTimeout(() => res(x * 2), 100));2const profiled = FunctionWrapper.stats(slowFn);4profiled(2).then(() => {5 console.log(profiled.stats); // { calls: 1, avgTime: '100.00ms' }6});
API Reference
Each static method of FunctionWrapper can be used independently to wrap any function. Refer to the method list in the Features section for full capabilities.
Chaining Example
1const fn = (x) => x * 2;2const chained = FunctionWrapper.chain(fn)3 .debounce(200)4 .log()5 .memo()6 .time()7 .value();9chained(5);
FunctionWrapper API Reference
FunctionWrapper is an advanced utility class that provides 80+ higher-order function decorators and wrappers for logging, timing, retrying, caching, debouncing, throttling, validating, transforming, and managing functions in versatile ways.
| Method | Parameters | Description | Usage Example | Output |
|---|---|---|---|---|
log(fn) |
fn: Function |
Logs function call and result | const add = (a,b)=>a+b; const loggedAdd = FunctionWrapper.log(add); loggedAdd(2,3); |
Logs: Calling add [2,3] Logs: Result from add 5 |
time(fn) |
fn: Function |
Measures execution time (async supported) | const slow = () => new Promise(res=>setTimeout(()=>res(42),100)); const timed = FunctionWrapper.time(slow); timed(); |
Logs: slow took 100.00 ms |
retry(fn, retries=3, delayMs=0) |
fn: Function, retries?: number, delayMs?: number |
Retries a failing function up to retries times with optional delay |
const unstable = ()=>{ if(Math.random()<0.7) throw Error('Fail'); return 'Ok'; }; const reliable = FunctionWrapper.retry(unstable,5,100); reliable().then(console.log) |
Either 'Ok' or error if all retries fail |
memo(fn) |
fn: Function |
Caches function results based on arguments | const square = x=>x*x; const cached = FunctionWrapper.memo(square); cached(2); cached(2); |
Only first call computes, second returns cached value |
debounce(fn, wait=300) |
fn: Function, wait?: number |
Delays execution until wait ms of inactivity |
const save = x=>console.log('Saved',x); const dSave = FunctionWrapper.debounce(save,500); dSave(1); dSave(2); |
Only 'Saved 2' after 500ms |
throttle(fn, wait=300) |
fn: Function, wait?: number |
Ensures function executes at most once per wait ms |
const log = x=>console.log(x); const tLog = FunctionWrapper.throttle(log,500); tLog(1); tLog(2); |
Executes 1 immediately, 2 after 500ms |
validate(fn, validator) |
fn: Function, validator: Function |
Validates arguments before calling | const sum = (a,b)=>a+b; const validated = FunctionWrapper.validate(sum,args=>{if(args.some(x=>typeof x!=='number'))throw Error('Invalid');}); validated(1,2); |
Returns 3 |
catch(fn, onError, fallback=null) |
fn: Function, onError?: Function, fallback?: any |
Catches errors, calls onError, optionally returns fallback |
const fail = ()=>{throw Error('Oops')}; const safe = FunctionWrapper.catch(fail,e=>console.log(e.message),'F'); safe(); |
Logs 'Oops' and returns 'F' |
once(fn) |
fn: Function |
Executes function only once globally | const inc=()=>1; const onceInc=FunctionWrapper.once(inc); onceInc(); onceInc(); |
Returns 1 both calls, only first actually executes |
after(fn, n=1) |
fn: Function, n?: number |
Executes function after n calls |
const log=()=>console.log('Hello'); const after2=FunctionWrapper.after(log,2); after2(); after2(); |
Logs 'Hello' on second call |
before(fn, n=1) |
fn: Function, n?: number |
Executes function at most n times |
const log=()=>console.log('Hi'); const b4=FunctionWrapper.before(log,2); b4(); b4(); b4(); |
Logs 'Hi' twice, third call returns null |
delayFn(fn, delay=1000) |
fn: Function, delay?: number |
Executes function after a delay | const say=()=>console.log('Hi'); const delayed=FunctionWrapper.delayFn(say,500); delayed(); |
Logs 'Hi' after 500ms |
timeLimit(fn, timeout=1000) |
fn: Function, timeout?: number |
Rejects if execution exceeds timeout | const slow=()=>new Promise(res=>setTimeout(()=>res(1),1500)); FunctionWrapper.timeLimit(slow,1000) |
Throws 'Timeout exceeded' |
tap(fn, tapFn) |
fn: Function, tapFn: Function |
Executes tapFn with function result without affecting it |
const double=x=>x*2; const tapped=FunctionWrapper.tap(double,r=>console.log('Tap',r)); tapped(3); |
Logs 'Tap 6', returns 6 |
restrict(fn, isAllowed) |
fn: Function, isAllowed: Function |
Throws if isAllowed returns false |
const secret=()=>42; const restricted=FunctionWrapper.restrict(secret,()=>false); restricted(); |
Throws 'Access denied' |
lock(fn) |
fn: Function |
Prevents concurrent execution | const sleep=x=>new Promise(res=>setTimeout(res,x)); const locked=FunctionWrapper.lock(sleep); locked(100); locked(100); |
Second call throws 'Already running' |
history(fn) |
fn: Function |
Tracks inputs and outputs | const add=(a,b)=>a+b; const h=FunctionWrapper.history(add); h(1,2); h.getHistory(); |
Returns [ { args:[1,2], result:3 } ] |
watch(fn) |
fn: Function |
Allows subscribing to results | const add=(a,b)=>a+b; const w=FunctionWrapper.watch(add); w.subscribe((args,res)=>console.log('Watched',res)); w(2,3); |
Logs 'Watched 5' |
mask(fn, masker) |
fn: Function, masker: Function |
Transforms arguments before calling | const sum=(a,b)=>a+b; const masked=FunctionWrapper.mask(sum,args=>args.map(x=>x*2)); masked(1,2); |
Returns 6 |
cancelable(fn) |
fn: Function |
Returns a cancelable promise | const p=()=>new Promise(res=>setTimeout(()=>res(1),500)); const c=FunctionWrapper.cancelable(p); const promise=c(); promise.cancel(); |
Rejects with 'Cancelled' |
limit(fn, max=5) |
fn: Function, max?: number |
Limits number of executions | const log=x=>x; const limited=FunctionWrapper.limit(log,2); limited(1); limited(2); limited(3); |
Third call throws 'Limit exceeded' |
rateTracker(fn, windowMs=1000) |
fn: Function, windowMs?: number |
Tracks number of calls in sliding window | const add=(a,b)=>a+b; const tracked=FunctionWrapper.rateTracker(add); tracked(1,2); |
Result object has _rate property |
replay(fn) |
fn: Function |
Returns cached result for identical arguments | const f=x=>x*2; const r=FunctionWrapper.replay(f); r(2); r(2); |
Returns 4 both times, second call uses cached result |
safeJson(fn) |
fn: Function |
Wraps function to catch JSON errors | const parse=JSON.parse; const safeParse=FunctionWrapper.safeJson(parse); safeParse('invalid'); |
Returns { error: 'Invalid JSON', details: ... } |
profile(fn) |
fn: Function |
Logs execution stats in table | const slow=x=>new Promise(res=>setTimeout(()=>res(x),100)); FunctionWrapper.profile(slow)(5); |
Logs table with function name, duration, timestamp |
unit(fn, unit='') |
fn: Function, unit?: string |
Returns { value, unit, timestamp } |
const add=(a,b)=>a+b; const u=FunctionWrapper.unit(add,'ms'); u(1,2); |
{ value:3, unit:'ms', timestamp: 12345678 } |
changelog(fn) |
fn: Function |
Logs changes in object output | const obj=()=>({x:Math.random()}); const c=FunctionWrapper.changelog(obj); c(); c(); |
Logs only changes |
undoable(fn, inverseFn) |
fn: Function, inverseFn: Function |
Supports undo operation | let x=0; const inc=()=>x++; const dec=()=>x--; const u=FunctionWrapper.undoable(inc,dec); u(); u.undo(); |
x returns to previous state |
feedback(fn, logger) |
fn: Function, logger?: Function |
Logs input/output with custom logger | const add=(a,b)=>a+b; const f=FunctionWrapper.feedback(add); f(1,2); |
Logs { input:[1,2], output:3 } |
locale(fn, formatter) |
fn: Function, formatter?: {input?: fn, output?: fn} |
Formats input/output | const add=(a,b)=>a+b; const loc=FunctionWrapper.locale(add,{output:r=>'Result:'+r}); loc(1,2); |
Returns 'Result:3' |
queue(fn) |
fn: Function |
Executes function sequentially | const sleep=x=>new Promise(res=>setTimeout(()=>res(x),100)); const q=FunctionWrapper.queue(sleep); q(1); q(2); |
Second call waits until first finishes |
repeat(fn, n=1) |
fn: Function, n?: number |
Calls function n times, returns array of results |
const f=x=>x*2; FunctionWrapper.repeat(f,3)(2); |
[4,4,4] |
test(fn, testCases=[]) |
fn: Function, testCases:Array<[input,expected]> |
Runs test cases | const add=(a,b)=>a+b; FunctionWrapper.test(add,[[ [1,2],3 ]])(); |
[ { input:[1,2], expected:3, result:3, passed:true } ] |
delayResult(fn, ms=1000) |
fn: Function, ms?: number |
Returns result after delay | const f=x=>x+1; FunctionWrapper.delayResult(f,500)(2); |
Returns 3 after 500ms |
ensure(fn, validator) |
fn: Function, validator: Function |
Throws if output fails validation | const f=x=>x*2; const e=FunctionWrapper.ensure(f,r=>r<5); e(2); e(3); |
Throws on second call |
randomizeArgs(fn) |
fn: Function |
Shuffles arguments randomly before calling | const f=(a,b)=>[a,b]; FunctionWrapper.randomizeArgs(f)(1,2); |
Returns [2,1] or [1,2] |
randomBehavior(fn, behaviors=[]) |
fn: Function, behaviors: Function[] |
Executes one of the random behaviors | const f=x=>x; const b=[(fn,args)=>fn(...args)*2]; FunctionWrapper.randomBehavior(f,b)(2); |
Returns 4 |
transformOutput(fn, transformer) |
fn: Function, transformer: Function |
Transforms function output | const f=x=>x+1; FunctionWrapper.transformOutput(f,r=>r*2)(2); |
Returns 6 |
evolve(fn, evolver) |
fn: Function, evolver: Function |
Mutates or extends output | const f=()=>({a:1}); FunctionWrapper.evolve(f,{b:2})(); |
{ a:1, b:2 } |
simulate(fn, {failRate=0.1, corrupt=false, delay=300}) |
fn: Function, options |
Simulates failures, delays, and corruption | const f=x=>x; FunctionWrapper.simulate(f,{failRate:1})(1) |
Throws 'Simulated Failure' |
chainable(fn) |
fn: Function |
Allows chaining calls and collecting results | const f=x=>x*2; const c=FunctionWrapper.chainable(f); c(1)(2).value(); |
Returns [2,4] |
predict(fn, modelFn) |
fn: Function, modelFn: Function |
Adds prediction based on function output | const f=x=>x*2; const p=FunctionWrapper.predict(f,r=>r+1); p(2); |
{ result:4, prediction:5 } |
delayIf(fn, condition, delayMs=1000) |
fn: Function, condition: Function, delayMs?: number |
Delays execution if condition is true | const f=x=>x; FunctionWrapper.delayIf(f,x=>x>0,500)(1); |
Returns 1 after 500ms |
eventual(fn, checkFn, interval=100) |
fn: Function, checkFn: Function, interval?: number |
Polls until checkFn returns true | const f=x=>x; FunctionWrapper.eventual(f,()=>true)(); |
Resolves immediately |
pipe(...fns) |
fns: Function[] |
Composes multiple functions left to right | FunctionWrapper.pipe(x=>x+1,x=>x*2)(2); |
Returns (2+1)*2 = 6 |
filterArgs(fn, filterFn) |
fn: Function, filterFn: Function |
Calls fn only if filterFn returns true | const f=x=>x; FunctionWrapper.filterArgs(f,x=>x>0)(-1); |
Returns null |
warnOnArgs(fn, warningFn) |
fn: Function, warningFn: Function |
Warns if warningFn returns true | const f=x=>x; FunctionWrapper.warnOnArgs(f,x=>x<0)(-1); |
Logs warning, returns -1 |
oncePerArgs(fn) |
fn: Function |
Executes once per argument combination | const f=x=>x; FunctionWrapper.oncePerArgs(f)(1); FunctionWrapper.oncePerArgs(f)(1); |
Returns 1, then null |
alertOn(fn, condition) |
fn: Function, condition: Function |
Alerts if condition on result is true | const f=x=>x; FunctionWrapper.alertOn(f,r=>r>0)(1); |
Logs alert, returns 1 |
hook(fn, { before, after }) |
fn: Function, before?: Function, after?: Function |
Adds pre- and post-execution hooks | const f=x=>x*2; FunctionWrapper.hook(f,{before:x=>console.log('Before',x),after:(r)=>console.log('After',r)})(2); |
Logs 'Before 2', 'After 4', returns 4 |
delayEach(fn, delayMs=300) |
fn: Function, delayMs?: number |
Calls function for each argument sequentially with delay | const f=x=>x*2; FunctionWrapper.delayEach(f,100)(1,2,3); |
Returns [2,4,6] over 100ms intervals |
sandbox(fn, timeout=1000) |
fn: Function, timeout?: number |
Executes safely with error handling | const f=()=>{throw Error('Fail')}; FunctionWrapper.sandbox(f)(); |
Logs 'Sandbox Error: Fail', returns undefined |
afterIdle(fn, timeout=0) |
fn: Function, timeout?: number |
Executes when browser idle | FunctionWrapper.afterIdle(()=>console.log('Idle'),500) |
Logs 'Idle' after idle period |
smartIdle(fn, {timeout=1000}) |
fn: Function, options |
Idle execution with cancellation | const f=x=>x; const s=FunctionWrapper.smartIdle(f); s(1); s.cancel(); |
Cancels execution if idle hasn't started |
| Method | Purpose |
|---|---|
log(fn) |
Logs function calls and results |
profile(fn) |
Measures execution time, logs stats |
changelog(fn) |
Logs differences between successive outputs |
feedback(fn, logger) |
Logs input/output with optional custom logger |
warnOnArgs(fn, warningFn) |
Warns if arguments meet suspicious condition |
alertOn(fn, condition) |
Alerts if output meets condition |
| Method | Purpose |
|---|---|
time(fn) |
Measures execution time (async supported) |
timeLimit(fn, timeout) |
Throws if execution exceeds timeout |
delayFn(fn, delay) |
Delays execution by specified ms |
delayResult(fn, ms) |
Returns result after delay |
delayEach(fn, delayMs) |
Sequentially calls function on multiple args with delay |
delayIf(fn, condition, delayMs) |
Delays execution if condition is true |
afterIdle(fn, timeout) |
Executes when browser is idle |
smartIdle(fn, {timeout}) |
Idle execution with cancellation support |
| Method | Purpose |
|---|---|
retry(fn, retries, delayMs) |
Retries function on failure |
once(fn) |
Executes only once |
lock(fn) |
Prevents concurrent executions |
queue(fn) |
Ensures sequential execution |
cancelable(fn) |
Returns cancelable promise |
sandbox(fn, timeout) |
Safe async execution with error handling |
eventual(fn, checkFn, interval) |
Polls until a condition is true |
simulate(fn, options) |
Simulates failures, delays, or corrupted outputs |
| Method | Purpose |
|---|---|
memo(fn) |
Caches results by arguments |
replay(fn) |
Returns cached result for identical args |
oncePerArgs(fn) |
Executes once per unique args |
history(fn) |
Tracks inputs and outputs |
tap(fn, tapFn) |
Allows side effects without changing output |
| Method | Purpose |
|---|---|
validate(fn, validator) |
Validates arguments before execution |
ensure(fn, validator) |
Validates output |
restrict(fn, isAllowed) |
Throws if access is denied |
| Method | Purpose |
|---|---|
debounce(fn, wait) |
Executes only after idle period |
throttle(fn, wait) |
Limits execution rate |
rateTracker(fn, windowMs) |
Tracks call frequency |
| Method | Purpose |
|---|---|
pipe(...fns) |
Composes multiple functions |
transformOutput(fn, transformer) |
Transforms result |
evolve(fn, evolver) |
Mutates output |
randomizeArgs(fn) |
Randomly shuffles arguments |
randomBehavior(fn, behaviors) |
Executes random behavior |
| Method | Purpose |
|---|---|
batch(fn, chunkSize, cb) |
Calls function in chunks |
repeat(fn, n) |
Calls function multiple times |
| Method | Purpose |
|---|---|
after(fn, n) |
Executes after n calls |
before(fn, n) |
Executes at most n times |
hook(fn, {before, after}) |
Hooks before/after execution |
undoable(fn, inverseFn) |
Supports undo operations |
chainable(fn) |
Allows chaining multiple calls |
pipe(...fns) |
Composes functions in sequence |
| Method | Purpose |
|---|---|
test(fn, testCases) |
Runs test cases |
simulate(fn, {failRate, corrupt, delay}) |
Simulates failures and delays |
predict(fn, modelFn) |
Adds predictions based on output |
| Method | Purpose |
|---|---|
unit(fn, unit) |
Wraps output with value, unit, timestamp |
mask(fn, masker) |
Masks or transforms arguments |
locale(fn, formatter) |
Localizes input/output |
Perfect! Letโs make a detailed mental map / diagram of the entire FunctionWrapper class and its methods. Iโll structure it clearly so you can either visualize it or later draw it on paper/software.
-
Provides wrappers/enhancements for functions
-
Key capabilities:
- Logging, timing, retry, caching
- Control execution (debounce, throttle, lock, once)
- Transform input/output
- Error handling and safety
- Composition and chaining
A. Logging & Monitoring
log(fn)โ logs calls and resultstime(fn)โ measures execution timeprofile(fn)โ detailed table of durationchangelog(fn)โ logs changes in object outputsstats(fn)โ tracks number of calls, avg timewatch(fn)โ subscribe to outputsfeedback(fn, logger)โ logs input/output
B. Execution Control
once(fn)โ runs only onceafter(fn, n)โ runs after n callsbefore(fn, n)โ runs at most n timeslimit(fn, max)โ limits number of callslock(fn)โ prevents concurrent executiononcePerArgs(fn)โ runs once per argument comboqueue(fn)โ sequential executiondelayFn(fn, delay)โ delay executiondelayResult(fn, ms)โ delay return valuedelayEach(fn, delayMs)โ sequential with delayafterIdle(fn, timeout)โ browser idle executionsmartIdle(fn, options)โ idle execution with cancel
C. Error Handling & Safety
catch(fn, onError, fallback)โ error catchsandbox(fn, timeout)โ safe execution with error handlingsafeJson(fn)โ catches JSON parsing errorstimeLimit(fn, timeout)โ rejects after timeoutcancelable(fn)โ cancel in-flight promise
D. Input/Output Transformation
validate(fn, validator)โ validate argumentsmask(fn, masker)โ transform argumentsrestrict(fn, isAllowed)โ allow/disallow calltap(fn, tapFn)โ side effect without affecting returntransformOutput(fn, transformer)โ change outputlocale(fn, formatter)โ format input/outputunit(fn, unit)โ wraps output with value/unit/timestamp
E. Caching & Memoization
memo(fn)โ caches resultsreplay(fn)โ returns cached result for identical args
F. Functional Composition
pipe(...fns)โ left-to-right compositionchainable(fn)โ chain calls, collect results
G. Retry & Simulation
retry(fn, retries, delayMs)โ retry failing fnsimulate(fn, options)โ simulate failure/delay/corruptionrandomBehavior(fn, behaviors)โ random behaviorrandomizeArgs(fn)โ shuffle arguments
H. Undo & History
undoable(fn, inverseFn)โ supports undohistory(fn)โ tracks args/results
I. Conditional Execution
ensure(fn, validator)โ ensures output passesfilterArgs(fn, filterFn)โ executes only if condition passesdelayIf(fn, condition, delayMs)โ delays conditionallyalertOn(fn, condition)โ logs alerts if condition truewarnOnArgs(fn, warningFn)โ warns on suspicious inputpredict(fn, modelFn)โ adds prediction based on outputeventual(fn, checkFn, interval)โ polls until condition true
J. Repeat & Batch
repeat(fn, n)โ repeat function n timesbatch(fn, chunkSize, cb)โ executes in chunks
K. Miscellaneous
hook(fn, {before, after})โ pre/post hooksevolve(fn, evolver)โ mutate or extend output
FunctionWrapper
โ
โโ Logging & Monitoring
โ โโ log()
โ โโ time()
โ โโ profile()
โ โโ changelog()
โ โโ stats()
โ โโ watch()
โ โโ feedback()
โ
โโ Execution Control
โ โโ once()
โ โโ after()
โ โโ before()
โ โโ limit()
โ โโ lock()
โ โโ oncePerArgs()
โ โโ queue()
โ โโ delayFn()
โ โโ delayResult()
โ โโ delayEach()
โ โโ afterIdle()
โ โโ smartIdle()
โ
โโ Error Handling & Safety
โ โโ catch()
โ โโ sandbox()
โ โโ safeJson()
โ โโ timeLimit()
โ โโ cancelable()
โ
โโ Input/Output Transformation
โ โโ validate()
โ โโ mask()
โ โโ restrict()
โ โโ tap()
โ โโ transformOutput()
โ โโ locale()
โ โโ unit()
โ
โโ Caching & Memoization
โ โโ memo()
โ โโ replay()
โ
โโ Functional Composition
โ โโ pipe()
โ โโ chainable()
โ
โโ Retry & Simulation
โ โโ retry()
โ โโ simulate()
โ โโ randomBehavior()
โ โโ randomizeArgs()
โ
โโ Undo & History
โ โโ undoable()
โ โโ history()
โ
โโ Conditional Execution
โ โโ ensure()
โ โโ filterArgs()
โ โโ delayIf()
โ โโ alertOn()
โ โโ warnOnArgs()
โ โโ predict()
โ โโ eventual()
โ
โโ Repeat & Batch
โ โโ repeat()
โ โโ batch()
โ
โโ Miscellaneous
โโ hook()
โโ evolve()
FunctionWrapper
โโ Logging & Monitoring
โ โโ log(fn)
โ โโ time(fn)
โ โโ profile(fn)
โ โโ changelog(fn)
โ โโ feedback(fn, logger)
โ โโ watch(fn)
โ
โโ Execution Control
โ โโ once(fn)
โ โโ after(fn, n)
โ โโ before(fn, n)
โ โโ lock(fn)
โ โโ queue(fn)
โ โโ repeat(fn, n)
โ โโ delayFn(fn, delay)
โ โโ timeLimit(fn, timeout)
โ โโ sandbox(fn, timeout)
โ โโ afterIdle(fn, timeout)
โ โโ smartIdle(fn, options)
โ
โโ Error Handling & Safety
โ โโ catch(fn, onError, fallback)
โ โโ safeJson(fn)
โ โโ undoable(fn, inverseFn)
โ โโ cancelable(fn)
โ โโ ensure(fn, validator)
โ โโ restrict(fn, isAllowed)
โ
โโ Input/Output Transformation
โ โโ tap(fn, tapFn)
โ โโ mask(fn, masker)
โ โโ transformOutput(fn, transformer)
โ โโ evolve(fn, evolver)
โ โโ locale(fn, formatter)
โ โโ randomizeArgs(fn)
โ โโ randomBehavior(fn, behaviors)
โ โโ predict(fn, modelFn)
โ
โโ Caching & Memoization
โ โโ memo(fn)
โ โโ replay(fn)
โ โโ oncePerArgs(fn)
โ
โโ Functional Composition
โ โโ chainable(fn)
โ โโ pipe(...fns)
โ โโ hook(fn, {before, after})
โ
โโ Retry & Simulation
โ โโ retry(fn, retries, delayMs)
โ โโ simulate(fn, {failRate, corrupt, delay})
โ
โโ Conditional Execution
โ โโ delayIf(fn, condition, delayMs)
โ โโ eventual(fn, checkFn, interval)
โ โโ filterArgs(fn, filterFn)
โ โโ warnOnArgs(fn, warningFn)
โ โโ alertOn(fn, condition)
โ
โโ Repeat & Batch
โ โโ batch(fn, chunkSize, cb)
โ โโ delayEach(fn, delayMs)
โ
โโ Unit & Stats
โโ unit(fn, unit)
โโ stats(fn)
๐ isMain & runIfMain
hbh-nodes provides utility functions to detect if a module is the main entry point and to execute code only when the module is run directly, not when imported. This is especially useful for libraries that can be both imported and executed as scripts.
Import
CommonJS
1const { isMain, runIfMain } = require("hbh-nodes");
ES Modules
1import { isMain, runIfMain } from "hbh-nodes";
isMain(importMetaUrl, cjsModule, mainModule)
Determines if the current module is being executed as the main script.
Parameters
| Name | Type | Description |
|---|---|---|
importMetaUrl |
string |
URL |
cjsModule |
object |
The CommonJS module object. Optional if using ESM. |
mainModule |
object |
The main module reference, typically require.main. Optional but recommended in CommonJS. |
Returns
trueโ If the module is the main entry point.falseโ If the module is imported or parameters are invalid.
Behavior
-
CommonJS Detection:
1if (typeof mainModule !== "undefined" && cjsModule === mainModule) {2 return require.main === mainModule;3}Checks if the current
moduleis the main script. -
ES Module Detection:
1const filename = fileURLToPath(importMetaUrl);2return filename === process.argv[1];Converts
import.meta.urlto a path and compares with the Node.js entry file. -
Safe Fallback: Returns
falsefor unknown or invalid inputs.
Example
1const { isMain } = require("./mainCheck");3if (isMain(null, module, require.main)) {4 console.log("This module is running directly!");5}
Edge Cases
- Works for both ES modules and CommonJS.
- Returns
falsewhen the module is imported. - Handles invalid inputs gracefully.
- In ESM,
requireis unavailable, so CommonJS checks are skipped.
runIfMain(importMetaUrl, cjsModule, mainModule, fn)
Executes a function only if the current module is the main script.
Parameters
| Name | Type | Description |
|---|---|---|
importMetaUrl |
string |
URL |
cjsModule |
object |
The CommonJS module object. Optional if using ESM. |
mainModule |
object |
The main module reference (require.main). Optional but recommended in CommonJS. |
fn |
function |
Function to execute if the module is main. |
Returns
- Returns the result of
fn()if executed. - Returns
undefinedif the module is imported.
Behavior
- Calls
isMain()internally to determine if the module is the main entry point. - Executes the provided function
fn()only when the module is running directly. - Safe for mixed ESM + CommonJS environments.
Example
CommonJS
1const { runIfMain } = require("./mainCheck");3function main() {4 console.log("Running directly as a script!");5}7runIfMain(null, module, require.main, main);
ES Module
1import { runIfMain } from "./mainCheck.mjs";3runIfMain(import.meta.url, null, null, () => {4 console.log("Running directly in ESM!");5});
Edge Cases
- Does nothing if the module is imported.
- Safely handles invalid inputs.
- Useful for CLI scripts or libraries with optional initialization.
Best Practices
- Always pass the correct module references in CommonJS (
moduleandrequire.main). - For ESM, pass
import.meta.urlandnullfor the CommonJS parameters. - Wrap all startup code or CLI logic inside
runIfMainto avoid side effects on import. - Use
isMainif you need conditional checks without running a function.
Advanced / Deep Dive Usage
Example: CLI Script with Arguments
1runIfMain(import.meta.url, module, require.main, () => {2 const args = process.argv.slice(2);3 console.log("CLI arguments:", args);4 // Start your CLI logic here5});
๐๏ธ JsonManager
A utility class for managing JSON data in memory and on disk. Supports loading, saving, reading, updating, and deleting nested keys using dot-separated paths.
Import
1import { JsonManager } from "hbh-nodes";
constructor(initialData = {})
Creates a new JsonManager instance with optional initial data.
Parameters:
initialData(object, optional) โ Starting JSON data. Defaults to an empty object.
Example:
1const jm = new JsonManager({ user: { name: "Alice" } });
static async loadFromFile(filePath)
Loads JSON data from a file and returns a JsonManager instance.
Parameters:
filePath(string) โ Path to the JSON file.
Returns:
Promise<JsonManager> โ A new instance containing the loaded data.
Example:
1const jm = await JsonManager.loadFromFile("./data.json");
Edge Cases:
- Throws an error if the file does not exist or contains invalid JSON.
async saveToFile(filePath)
Saves the current JSON data to a file.
Parameters:
filePath(string) โ Path where the JSON should be written.
Returns:
Promise<void>
Example:
1await jm.saveToFile("./data.json");
Edge Cases:
- Overwrites the file if it already exists.
- Throws an error if the write operation fails.
get(path)
Retrieves a value from nested JSON using a dot-separated path.
Parameters:
path(string) โ Dot-separated key path (e.g.,"user.name").
Returns:
The value at the given path or undefined if not found.
Example:
1const name = jm.get("user.name"); // "Alice"
set(path, value)
Sets a value in nested JSON using a dot-separated path. Creates intermediate objects if needed.
Parameters:
path(string) โ Dot-separated key path.valueโ Value to set.
Example:
1jm.set("user.age", 25);
delete(path)
Deletes a key in nested JSON using a dot-separated path.
Parameters:
path(string) โ Dot-separated key path.
Example:
1jm.delete("user.age");
print()
Pretty-prints the current JSON data to the console.
Example:
1jm.print();2/*3{4 "user": {5 "name": "Alice"6 }7}8*/
๐ฏ micromatch
A minimal utility for matching strings against glob patterns. Supports standard wildcards (*, **, ?) and character ranges ([a-z]). Can also negate patterns using !.
Import
1import { micromatch } from "hbh-nodes";
escapeRegex(str)
Escapes special regex characters in a string.
Parameters:
str(string) โ Input string.
Returns: Escaped string safe for regex.
Example:
1const escaped = micromatch.escapeRegex("file?.txt"); // "file\?.txt"
globToRegex(pattern)
Converts a glob pattern to a JavaScript RegExp.
Parameters:
pattern(string) โ Glob pattern (e.g.,"*.js"or"src/**/index.js").
Returns:
RegExp โ Equivalent regex for matching.
Example:
1const regex = micromatch.globToRegex("src/**/*.js");2console.log(regex.test("src/app/index.js")); // true
Edge Cases:
*matches any sequence except/.**matches any sequence including/.?matches any single character except/.- Character ranges like
[a-z]are preserved.
micromatch(list, pattern)
Filters a list of strings based on a glob pattern. Supports negation with a leading !.
Parameters:
list(string[]) โ Array of strings to match.pattern(string) โ Glob pattern.
Returns: Array of matched strings.
Example:
1const files = ["index.js", "style.css", "app.js"];2console.log(micromatch.micromatch(files, "*.js")); // ["index.js", "app.js"]3console.log(micromatch.micromatch(files, "!*.js")); // ["style.css"]
matchGlob(pattern, str)
Tests if a single string matches a glob pattern.
Parameters:
pattern(string) โ Glob pattern.str(string) โ String to test.
Returns:
boolean โ true if the string matches the pattern.
Example:
1console.log(micromatch.matchGlob("*.js", "index.js")); // true2console.log(micromatch.matchGlob("*.js", "style.css")); // false
๐ Project2MD
Overview
The Project2MD function converts a project directory into a single Markdown (.md) file. It recursively walks through the directory tree, reads file contents, and appends them to a Markdown file with optional formatting. It also generates a summary table of all processed files.
This is useful for creating project documentation, code snapshots, or sharing projects as a single Markdown file.
Installation
1npm install hbh-nodes
Or using yarn:
1yarn add hbh-nodes
Usage
1import { P2PMD } from 'hbh-nodes';3const { Project2MD, SimpleExclude } = P2PMD;5await Project2MD({6 input: './my-project',7 output: './docs/project.md',8 concurrency: 10,9 onprogress: ({ index, relPath }) => {10 console.log(`Processed file #${index}: ${relPath}`);11 },12 onEnd: ({ total }) => {13 console.log(`Finished! Total files processed: ${total}`);14 },15});
Function Signature
1async function Project2MD(opts?: Project2MDOptions): Promise<void>
Parameters
opts โ An optional object of type Project2MDOptions.
Options
| Option | Type | Default | Description | |
|---|---|---|---|---|
input |
string |
'./' |
Path to the input directory to convert. | |
output |
string |
'output.md' |
Path of the Markdown file to create. | |
onprogress |
(progress: { index: number; relPath: string }) => void |
() => {} |
Callback called after each file is processed. | |
onEnd |
(summary: { total: number }) => void |
() => {} |
Callback called when processing finishes. | |
filenameFormatter |
`(relPath: string, stats: fs.Stats) => string | Promise |
Returns Markdown header for each file. Default: ###### \${relPath}`` |
Allows customizing the header of each file block. |
summaryFormatter |
`(files: { relPath: string; size: number }[]) => string | Promise |
Generates the summary table at the end. | |
exclude |
string[] |
SimpleExclude.Node |
List of files or directories to exclude (supports globs * and ?). |
|
ignoreHidden |
boolean |
true |
Skip hidden files and directories starting with .. |
|
concurrency |
number |
5 |
Maximum number of files to process concurrently. | |
maxFileSize |
number |
10 * 1024 * 1024 |
Skip files larger than this size (in bytes). | |
stopOnError |
boolean |
false |
If true, stops processing on the first error. |
Predefined Exclude
SimpleExclude is provided to skip common Node.js project files and directories:
1SimpleExclude.Node = ['node_modules', 'package-lock.json', '.gitignore', 'package.json'];
How It Works
-
Resolve Paths Converts
inputandoutputto absolute paths and ensures the output file itself is excluded. -
Directory Walk Recursively walks directories sequentially to avoid concurrency deadlocks.
- Directories are traversed first.
- Files are processed concurrently, respecting the
concurrencylimit.
-
File Processing For each file:
- Skips if hidden, excluded, or exceeds
maxFileSize. - Reads file contents (
utf-8). - Generates a Markdown header using
filenameFormatter. - Wraps the content in a fenced code block with language derived from the file extension.
- Appends content to the output Markdown file.
- Calls
onprogresscallback.
- Skips if hidden, excluded, or exceeds
-
Summary Generation After all files are processed:
- Calls
summaryFormatterwith the list of processed files. - Appends the generated summary to the Markdown file.
- Calls
-
Error Handling
- Wraps file system operations in
safeRunto catch errors without stopping execution unlessstopOnErroristrue. - Logs warnings and errors for files that fail to process.
- Wraps file system operations in
Example Output
Assuming a directory structure:
src/
โโ index.js
โโ utils.js
README.md
Generated output.md could look like:
1###### `src/index.js`3```js4console.log('Hello World');
src/utils.js
1export function add(a, b) { return a + b; }
README.md
1# My Project2This is my project.
Notes
- Files larger than
maxFileSizeare skipped. - Hidden files/directories are ignored if
ignoreHiddenistrue. - Concurrency ensures faster processing but directory traversal is always sequential.
- Markdown language blocks are derived from file extensions; fallback to no language if unknown.
onprogressandonEndallow progress tracking or UI updates.
This function is designed for Node.js environments and uses ESM imports (fs/promises, path).
๐ฆ packageJson
A set of functions to programmatically create and manage package.json files for Node.js projects, including batch creation in subdirectories and automated publishing.
Import
1import { JPM } from "hbh-nodes";2const { createPackageJson, createPackagesInSubdirs, publishAllPackages } = JPM;
createPackageJson(dirPath, newJson)
Creates a package.json file in the specified directory. If the file already exists, it will not overwrite it.
Parameters:
dirPath(string) โ Path to the directory wherepackage.jsonshould be created.newJson(object, optional) โ Overrides or additional fields for thepackage.json.
Returns:
- JSON string of the final package content.
Example:
1createPackageJson("./my-lib", {2 name: "my-lib",3 version: "1.0.0",4 author: "HBH"5});
Edge Cases:
- If a
package.jsonalready exists, it is not overwritten. - Default fields are merged with any custom fields provided.
createPackagesInSubdirs(parentDir, defaultJsonOverrides)
Creates package.json files in all subdirectories of a given directory.
Parameters:
parentDir(string) โ Directory containing multiple package folders.defaultJsonOverrides(object, optional) โ Fields to override default values for all packages.
Example:
1createPackagesInSubdirs("./packages", {2 license: "MIT",3 author: "HBH"4});
Edge Cases:
- Uses the folder name as the package
nameby default. - Only directories are processed; files are ignored.
- Existing
package.jsonfiles are preserved.
publishAllPackages(parentDir)
Publishes all Node.js packages in subdirectories under a specified directory using npm publish.
Parameters:
parentDir(string) โ Directory containing multiple package folders with validpackage.json.
Example:
1publishAllPackages("./packages");
Behavior:
- Runs
npm publish --access publicin each subdirectory. - Logs success or failure for each package.
Edge Cases:
- Subdirectories without
package.jsonare skipped. - Errors in publishing one package do not stop publishing of others.
- Output of
npm publishis displayed in the console.
๐งผ Sanitizers
A comprehensive utility to sanitize strings, objects, and arrays by removing or masking sensitive data such as emails, paths, IPs, URLs, credit cards, and more.
Import
1import { Sanitizers as SZ } from "hbh-nodes";2const { Sanitizers, deepSanitize, applySanitizers, sanitizeAll, capitalize, getSanitizerNames } = SZ;
Sanitizers
An object containing individual sanitization functions automatically generated from common sensitive patterns.
Example Functions:
sanitizeEmail(str)โ Masks emails.sanitizePath(str)โ Masks file paths.sanitizeIp(str)โ Masks IPv4/IPv6 addresses.sanitizeCreditCard(str)โ Masks credit card numbers.sanitizeHtml(str)โ Strips HTML tags.sanitizeJwt(str)โ Masks JWT tokens.
Usage:
1const dirty = "User email: test@example.com";2const clean = Sanitizers.sanitizeEmail(dirty);3console.log(clean); // User email: [SANITIZED:email]
deepSanitize(obj, sanitizerFns = [], sanitizeKeys = false)
Recursively sanitizes strings in objects or arrays using provided sanitizer functions.
Parameters:
objโ Object, array, or string to sanitize.sanitizerFnsโ Array of sanitizer functions to apply.sanitizeKeys(boolean) โ Iftrue, also sanitizes object keys.
Example:
1const data = {2 user: {3 email: "test@example.com",4 password: "1234"5 }6};8const clean = deepSanitize(data, [Sanitizers.sanitizeEmail]);9console.log(clean);10// { user: { email: '[SANITIZED:email]', password: '1234' } }
applySanitizers(value, sanitizers = [])
Applies an array of sanitizer functions to a single string safely.
Example:
1const dirty = "My email is test@example.com";2const clean = applySanitizers(dirty, [Sanitizers.sanitizeEmail]);3console.log(clean); // My email is [SANITIZED:email]
sanitizeAll(str, options = {})
Convenience function to apply multiple sanitizers with filtering and custom replacements.
Options:
onlyโ Array of sanitizer names to apply exclusively.excludeโ Array of sanitizer names to skip.replacementsโ Custom replacement strings for specific sanitizers.
Example:
1const dirty = "Contact me at test@example.com or 123 Main St";2const clean = sanitizeAll(dirty, { exclude: ["sanitizePath"] });3console.log(clean); // Contact me at [SANITIZED:email] or 123 Main St
capitalize(str)
Utility to capitalize the first letter of a string, mainly used for dynamically naming sanitizer functions.
Example:
1console.log(capitalize("email")); // Email
getSanitizerNames()
Returns an array of all generated sanitizer function names.
Example:
1console.log(getSanitizerNames());2// ["sanitizePath", "sanitizeEmail", "sanitizeIp", ...]
Edge Cases & Notes:
- Sanitizers operate safely on non-string values by returning them unchanged.
deepSanitizecan handle nested arrays and objects of arbitrary depth.- Custom replacements allow flexible masking beyond default
[SANITIZED:<type>].
๐๏ธ StringCompressor
A utility class for compressing and decompressing strings using a dictionary-based approach. Frequently occurring words can be replaced with short keys to reduce string size. Supports customizable flags, automatic dictionary generation, and statistics on compression efficiency.
Import
1import { StringCompressor } from "hbh-nodes";
Class: StringCompressor
Constructor
1new StringCompressor(dictionary = {}, flag = '$')
Parameters:
dictionaryโ Optional initial dictionary mapping original strings to compressed keys. Defaults to{}.flagโ Prefix for compressed keys. Defaults to$.
Example:
1const compressor = new StringCompressor({}, '#');
setDictionary(dictionary)
Sets a new dictionary and automatically generates a reverse dictionary for decompression.
Parameters:
dictionaryโ Object mapping original strings to short keys.
Example:
1compressor.setDictionary({ hello: '$a', world: '$b' });
compress(str, dictionary = null)
Compresses a string using the current dictionary. Optionally, a new dictionary can be passed.
Parameters:
strโ The string to compress.dictionaryโ Optional dictionary to override current one.
Example:
1const compressed = compressor.compress("hello world hello");2// "$a $b $a"
decompress(str, dictionary = null)
Decompresses a string using the current reverse dictionary. Optionally, a new dictionary can be passed.
Parameters:
strโ The string to decompress.dictionaryโ Optional dictionary to override current one.
Example:
1const decompressed = compressor.decompress("$a $b $a");2// "hello world hello"
generateMap(str, minLength = 4, minFreq = 2, flag)
Generates a compression dictionary automatically based on word frequency in the input string.
Parameters:
strโ Input string.minLengthโ Minimum length of words to consider. Defaults to4.minFreqโ Minimum frequency of words to include. Defaults to2.flagโ Optional flag prefix for keys. Defaults to the instance flag.
Returns: Dictionary object mapping words to short keys.
Example:
1const map = compressor.generateMap("hello world hello world", 3, 2);2// { hello: '$a', world: '$b' }
generateAndSetDictionary(str, minLength = 4, minFreq = 2, flag)
Generates a frequency-based dictionary and sets it as the current dictionary.
Returns: Dictionary object.
Example:
1compressor.generateAndSetDictionary("hello world hello world");2// Dictionary now set: { hello: '$a', world: '$b' }
stats(originalStr, compressedStr)
Provides statistics on compression efficiency.
Parameters:
originalStrโ Original string.compressedStrโ Compressed string.
Returns: Object with:
originalLengthโ Length of original string.compressedLengthโ Length of compressed string.savedโ Number of characters saved.percentSavedโ Percentage reduction as string.
Example:
1const stats = compressor.stats("hello world hello", "$a $b $a");2console.log(stats);3// { originalLength: 17, compressedLength: 9, saved: 8, percentSaved: '47.06%' }
replace(str, map)
Replaces occurrences of keys in a string based on a mapping object.
Example:
1compressor.replace("hello world", { hello: '$a', world: '$b' });2// "$a $b"
Edge Cases & Notes:
- Supports Unicode words via
\p{L}regex. - Generates short keys using patterns like
a, b, ..., z, aa, ab. - Words shorter than
minLengthor below frequency threshold are ignored. - Safe to use on strings without dictionary; returns original string unchanged if mapping is empty.
โฑ๏ธ TrackTimeWrapper โ Time Utilities
Measure execution time and format durations with ease.
๐ TrackTimeWrapper.formatDuration(ms)
Convert milliseconds into a human-readable duration string.
1import { TrackTimeWrapper } from 'hbh-nodes';3TrackTimeWrapper.formatDuration(65000); // "1 min 5 sec"4TrackTimeWrapper.formatDuration(3600000); // "1 hr 0 sec"
โจ Features
- Converts milliseconds to weeks, days, hours, minutes, seconds
- Clean output for logs & CLIs
- Zero dependencies
โฑ๏ธ TrackTimeWrapper.Wrapper(asyncFn, ...args)
Wrap any async function and return its execution time.
1import { TrackTimeWrapper } from 'hbh-nodes';3async function task() {4 await new Promise(res => setTimeout(res, 1200));5 return 'Done';6}8const { result, duration } =9 await TrackTimeWrapper.Wrapper(task);11console.log(result); // "Done"12console.log(duration); // "1 sec"
โจ Features
- Measures async function runtime
- Returns
{ result, duration } - Internally uses
TrackTimeWrapper.formatDuration - Ideal for benchmarking & performance logs
๐ก When to Use
- CLI tools
- Performance measurement
- Async task monitoring
- Developer debugging
๐ StringArrayManager
A robust class for managing arrays of strings with full support for validation, undo/redo, event handling, logging, and utility operations like search, pagination, and merging. It enforces uniqueness and optional case-insensitivity.
Import
1import { StringArrayManager } from "hbh-nodes";
Constructor
1new StringArrayManager(initialArray = [], options = {})
Parameters:
initialArrayโ Optional array of strings to initialize.optionsโ Optional configuration object:
| Option | Type | Default | Description |
|---|---|---|---|
| maxHistory | number | 100 | Maximum undo history length |
| caseInsensitive | boolean | false | Normalize strings to lowercase for comparison |
| maxItems | number | Infinity | Maximum number of strings allowed |
| validate | function | null | Function (value) => boolean for custom validation |
| returnThis | boolean | false | If true, mutating methods return this instead of status objects |
| saveHistory | boolean | true | Enable/disable saving history for undo/redo |
Example:
1const manager = new StringArrayManager(["apple", "banana"], { caseInsensitive: true });
Events
Subscribe to events emitted during operations:
1manager.on("add", ({ value }) => console.log("Added:", value));2manager.on("remove", ({ value }) => console.log("Removed:", value));3manager.off("add", callback); // Remove listener
Available events include: add, remove, replace, delete, clear, reset, sort, undo, redo, merge, addBulk, removeBulk.
Adding & Removing Strings
add(value)
Adds a single string if valid, unique, and within limits.
1manager.add("orange");
addBulk(values)
Adds multiple strings with detailed results for added and skipped items.
1manager.addBulk(["kiwi", "pear", "apple"]);
remove(value) / removeBulk(values)
Remove single or multiple strings.
1manager.remove("banana");2manager.removeBulk(["kiwi", "apple"]);
replace(oldValue, newValue)
Replace a string with a new one (valid & unique).
1manager.replace("apple", "grape");
delete(index)
Remove by index.
1manager.delete(0);
Array Utilities
sortManual(compareFn)
Sort array manually, optionally using a comparator.
1manager.sortManual(); // Default lexicographical2manager.sortManual((a, b) => a.length - b.length);
search(pattern)
Search strings using a substring or regex.
1manager.search("app"); // substring search2manager.search(/^g/); // regex search
clear() / reset(initialArray)
Clear all strings or reset to a new array.
1manager.clear();2manager.reset(["pear", "melon"]);
getAll() / length() / stats()
Retrieve array, length, or statistics (longest, shortest, average length).
1manager.getAll();2manager.length();3manager.stats();
Undo / Redo
1manager.undo();2manager.redo();
Supports undoing any change with full history up to maxHistory.
Logging
1manager.enableLogging(true);2manager.getLog();
Tracks operations with timestamps and details.
Validation
Custom validation function:
1manager.setValidation(val => val.length > 3 && /^[a-z]+$/.test(val));
JSON Serialization
1const json = manager.toJSON();2manager.fromJSON(json);
Utility Methods
has(value)โ Check if value exists.get(index)โ Get value by index.indexOf(value)โ Find index of value.clone()โ Create a new instance with same array and options.diff(prevArray)โ Compare current array with a previous array (added/removed).merge(values)โ Merge multiple strings with duplicate/limit handling.getPage(pageNum, pageSize)โ Paginate array.
Example Usage
1const manager = new StringArrayManager(["apple", "banana"], { caseInsensitive: true });3// Add strings4manager.add("Cherry");5manager.addBulk(["Date", "Fig"]);7// Remove strings8manager.remove("banana");10// Replace strings11manager.replace("apple", "Apricot");13// Undo / Redo14manager.undo();15manager.redo();17// Search18manager.search(/a/i);20// Pagination21manager.getPage(1, 2);23// Stats24manager.stats();26// Event listening27manager.on("add", ({ value }) => console.log("Added:", value));29// Logging30manager.enableLogging(true);31console.log(manager.getLog());
StringArrayManager is ideal for managing dynamic lists of strings with full control, safe operations, history tracking, and extensibility.
๐ฅ๏ธ wrapCLI
A lightweight wrapper for creating CLI (Command-Line Interface) commands in Node.js, with automatic argument parsing, type conversion, help support, and async handling.
Overview
wrapCLI allows you to wrap a regular JavaScript function and run it as a CLI command. It:
- Parses command-line arguments (
process.argv). - Supports positional and named arguments.
- Automatically converts argument strings to boolean, number, or string.
- Provides built-in help functionality with aliases.
- Handles async functions and errors gracefully.
Functions
1. tryParseValue(value)
Converts string values to their appropriate types.
1tryParseValue("true"); // โ true2tryParseValue("false"); // โ false3tryParseValue("123.5"); // โ 123.54tryParseValue("hello"); // โ "hello"
- Booleans (
true/false) - Numbers (
parseFloat) if numeric - Strings otherwise
2. parseArgs(argv)
Parses an array of CLI arguments into positional and named arguments.
Example:
1node cli.js foo bar --count 3 --verbose true
Output:
1{2 positional: ["foo", "bar"],3 named: { count: 3, verbose: true }4}
Behavior:
--key valuebecomesnamed[key] = value.- Multiple values after a flag become an array.
- Flag with no value โ
true. - Single-value arrays are simplified to a single value.
3. showHelp(meta)
Displays a help message using a metadata object.
1const meta = {2 description: "Example CLI tool",3 args: [{ name: "input", type: "string", required: true }],4 named: [{ name: "verbose", type: "boolean" }]5};7showHelp(meta);
Output:
๐ Example CLI tool
Positional arguments:
input (required) <string> โ
Named options:
--verbose <boolean> โ
Use "--help" to show this message.
4. wrapCLI(fn, meta, aliases)
Wraps a JavaScript function fn as a CLI command.
1import { wrapCLI } from 'hbh-nodes';3function greet(name, options) {4 const prefix = options?.loud ? "HELLO" : "Hello";5 return `${prefix} ${name}!`;6}8const cli = wrapCLI(greet, {9 description: "Greet someone",10 args: [{ name: "name", type: "string", required: true }],11 named: [{ name: "loud", type: "boolean" }]12});14cli(); // Run from Node.js
Features:
- Checks for help flags (
--helpor-h) viaaliases. - Spreads positional arguments to the function.
- Optionally passes named arguments as the last object.
- Handles async functions automatically.
- Catches exceptions and logs errors.
Example Usage
1# Positional argument2node cli.js Alice4# Named argument5node cli.js Alice --loud true7# Help8node cli.js --help9node cli.js -h
Notes
- Named arguments repeated are collected as arrays.
tryParseValueautomatically converts numeric and boolean strings.- Help output is fully customizable via the
metaobject.
This wrapper is perfect for lightweight CLI tools without installing external dependencies like yargs or commander.
It keeps argument parsing simple, type-safe, and easy to extend with aliases or async handlers.
๐งช Environment Support
- Node.js 18+
- ES Modules only
๐ License
ISC
โ๏ธ Author
HBH
๐ Final Words
HBH-Nodes is built for developers who value control, readability, and long-term maintainability.
If you like tools that stay out of your way, this package is for you โจ