JavaScript and NodeJS are missing a lot of core functionalities. The goal of this library is to bring a variety of useful helpers on both NodeJS & Browser with strong TypeScript typing and zero dependencies.
The package is isomorphic — every helper runs in browsers, Node, Deno, Bun, and workers. Runtime-specific APIs (e.g. Buffer) are feature-detected with portable fallbacks. The build is ESM with "sideEffects": false, so unused exports are tree-shaken.
Missing something? Create feature request!
Read Documentation 📘
# NPM
npm install common-stuff
# Yarn
yarn add common-stuff
Import what you need:
import { isEqual } from "common-stuff";
if (isEqual({ a: 1 }, { a: 1 })) {
console.log("Hello");
}
Always import only what is necessary to take full advantage of tree shaking.
The iterable module provides one set of helpers that work across Array, Set, Map, Record, generic Iterable, and AsyncIterable — preserving the input container type and switching to Promise automatically when the callback is async.
Available: map, flatMap, filter, reduce, forEach, find, some, every, size, toArray, take, drop, partition, zip.
import { map, flatMap, filter, reduce, partition, zip } from "common-stuff";
// Array → Array
map([1, 2, 3], (v) => v * 2);
// [2, 4, 6]
// Set → Set (preserves container)
filter(new Set([1, 2, 3, 4]), (v) => v % 2 === 0);
// Set { 2, 4 }
// Map → Map (callback receives [k, v])
map(new Map([["a", 1]]), ([k, v]) => [k.toUpperCase(), v * 10]);
// Map { 'A' => 10 }
// Record → Record (iterated as [k, v] entries)
flatMap({ a: 1, b: 2 }, ([k, v]) => (v % 2 ? [[k, v]] : []));
// { a: 1 }
// Async iterable + async mapper
async function* gen() {
yield 1;
yield 2;
yield 3;
}
await reduce(gen(), (a, b) => a + b);
// 6
// Partition into matching / non-matching halves
partition([1, 2, 3, 4], (v) => v % 2 === 0);
// [[2, 4], [1, 3]]
// Zip element-wise, stopping at the shortest
zip([1, 2, 3], ["a", "b"]);
// [[1, 'a'], [2, 'b']]
import {
pipe,
sortBy,
deduplicateBy,
chunk,
ensureArray,
groupBy,
reduce,
} from "common-stuff";
const result = pipe(
[{ value: 4 }, { value: 6 }, { value: 8 }],
(v) => sortBy(v, (o) => o.value),
(v) => deduplicateBy(v, (o) => o.value),
(v) => v.map((o) => ensureArray(o.value)),
(v) => chunk(v, 2),
(v) => groupBy(v, (o) => o.length)
);
// [ [1, [[[ 8 ]]]],[ 2, [[[ 4 ], [ 6 ]]]] ]
// Using reduce with async iterable
async function* gen() {
yield 1;
yield 2;
yield 3;
}
const total = await reduce(gen(), (a, b) => a + b); // 6
With arrow functions you can easily use
pipewith any function
For example we have following ENV variables:
CONFIG__PRIVATE_KEY="my key"
CONFIG__PUBLIC_KEY="my key"
CONFIG__ALLOWED_IPS='["127.0.0.1", "localhost"]'
import { convertToNested, camelCase } from "common-stuff";
const config = convertToNested(process.env, {
separator: "__",
transformKey: camelCase,
}).config;
// { privateKey: 'my key', publicKey: 'my key', allowedIps: ['127.0.0.1', 'localhost'] }
import { HttpError, HttpStatusCodes } from "common-stuff";
app.get("/", function (req, res) {
throw new HttpError(
HttpStatusCodes.INTERNAL_SERVER_ERROR,
"Some secret error message"
);
});
// Handle unknown errors
app.use(function (err, req, res, next) {
if (err instanceof HttpError) {
// Log full error message
console.error(err.message);
// Return safe error message without private details
return res.status(err.status).send(err.publicMessage);
}
next();
});
This example returns 500 error message with text
Internal Server Errorand logs private message to console. Check express-async-errors for Express JS async support.
import { parseCookies, generateCookie, parseQueryString } from "common-stuff";
parseCookies(document.cookie);
// {session: '26e761be168533cbf0742f8c295176c7'}
document.cookie = generateCookie("name", "John", { expires: 7 });
parseQueryString(location.search);
// { page: ['1'], limit: ['20']}
Breaking changes only:
require("common-stuff") no longer works.httpErrorHandler removed — inline the middleware (it was a thin instanceof HttpError wrapper).HttpStatusCodes / HttpStatusReasons are const objects, not enums (required by erasableSyntaxOnly). Value reads are unchanged; the type HttpStatusCodes is gone — use (typeof HttpStatusCodes)[keyof typeof HttpStatusCodes] if needed.flatMap removed. The universal flatMap (iterable module) requires callbacks to return an iterable and only passes (item, index).parseSize returns number | undefined instead of -1.merge<A, B>(target, source): A & B — return type is inferred. Explicit merge<MyShape>(a, b) calls need to pass both generics or cast.hashCode integers changed (prefixed with typeof to avoid falsy collisions). Invalidate any persisted hashes.difference / intersection / union / shuffle now correctly return T[] for mutable inputs (v1 always returned ReadonlyArray<T> due to overload order).isArray narrows readonly inputs to ReadonlyArray<T> (v1 narrowed them to mutable Array<T>); runtime switched to Array.isArray (fixes cross-realm).getByKey infers the return type from literal paths. Pass T explicitly for dynamic paths.