npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

craft-kit

v2.4.0

Published

All-in-one zero-dependency toolkit for JavaScript and TypeScript: data structures, algorithms, design patterns, an adapter-based HTTP client, Java-style utilities, functional helpers, async utilities, array/object/string/date helpers, validation (incl. Ch

Readme

craft-kit

npm version license types

English · Español · 📚 Full feature reference (tables)

All-in-one utility kit for JavaScript and TypeScript: data structures, algorithms, design patterns, an HTTP client, Java-style utilities, functional programming, async helpers, array/object/string/date utilities, validation (including Chilean RUT) and security.

  • Works in Node, the browser (with a bundler), Express, NestJS, Vite, etc.
  • ESM and CommonJS: import and require work with no configuration.
  • Types included, fully generic. Zero dependencies and tree-shakeable.

📚 Looking for the full list of everything the library exposes? See the feature reference.

📦 Installation

npm install craft-kit      # or: yarn add craft-kit / pnpm add craft-kit
import { quickSort, HttpClient, EventEmitter, rut, arrays, is } from "craft-kit"; // ESM
const { quickSort, HttpClient } = require("craft-kit");                            // CommonJS

🧱 Data structures

import { Stack, PriorityQueue, LRUCache, Trie, AVLTree, Graph } from "craft-kit";

new Stack<number>().push(1).push(2).pop();          // 2
new PriorityQueue<number>((a, b) => a - b);          // priority queue
new LRUCache<string, number>(100);                   // LRU cache
new AVLTree<number>();                                // self-balancing tree

Available: Stack, Queue, Deque, SinglyLinkedList, DoublyLinkedList, HashTable, BinarySearchTree, AVLTree, BinaryHeap/MinHeap/MaxHeap, PriorityQueue, Graph, UnionFind, LRUCache, TTLCache, Trie, BloomFilter, CircularBuffer, SkipList, SegmentTree, FenwickTree, StructureFactory.

🧮 Algorithms

import { quickSort, binarySearch, sorter } from "craft-kit";
quickSort([5, 3, 8, 1]);                 // [1, 3, 5, 8]   (10 sorting algorithms)
binarySearch([1, 2, 3, 4], 3);           // 2             (binary/linear/jump/interpolation)

Graph: bfs, dfs, dijkstra/shortestPath, aStar, bellmanFord, floydWarshall, topologicalSort, connectedComponents, kruskalMST/primMST, stronglyConnectedComponents, bridges, isBipartite, maxFlow.

import { Graph, shortestPath, aStar } from "craft-kit";
const g = new Graph(); g.addEdge("A", "B", 1).addEdge("B", "C", 2).addEdge("A", "C", 4);
shortestPath(g, "A", "C");               // { path: ["A","B","C"], distance: 3 }

Strings: levenshtein, similarity, kmpSearch, fuzzySearch. Dynamic programming: fibonacci, knapsack01, longestCommonSubsequence. Backtracking: solveNQueens, solveSudoku, solveMaze. Math: gcd, lcm, isPrime, primesUpTo, factorial, modPow, shuffle, stats, Vector, Matrix. Geometry: convexHull, pointInPolygon, distance.

import { levenshtein, stats, Vector } from "craft-kit";
levenshtein("kitten", "sitting");        // 3
stats.median([1, 2, 3, 4]);              // 2.5
new Vector([3, 4]).magnitude();          // 5

🎯 Design patterns

import { EventEmitter, StateMachine, CommandManager, ObjectPool, Signal, singleton } from "craft-kit";

// Observer
const bus = new EventEmitter<{ login: { user: string } }>();
bus.on("login", (p) => console.log(p.user));
bus.emit("login", { user: "ana" });

// State machine (FSM)
const sm = new StateMachine({ initial: "idle", transitions: { idle: { start: "run" }, run: { stop: "idle" } } });
sm.send("start"); sm.state;              // "run"

// Command with undo/redo
const cmd = new CommandManager();
cmd.execute({ execute: () => {}, undo: () => {} });
cmd.undo(); cmd.redo();

// Reactivity
const count = new Signal(0);
count.subscribe((v) => console.log(v));
count.set(1);

Also: PubSub, Chain (Chain of Responsibility), Mediator, Builder, Observable, computed.

🌐 HTTP client (Adapter pattern)

The transport is swappable: it uses fetch by default, but you can inject your own HttpAdapter (axios, node http, a mock for tests, etc.). Supports interceptors and retries.

import { HttpClient } from "craft-kit";

const api = new HttpClient({ baseURL: "https://api.example.com", timeout: 5000, retries: 2 });

api.useRequestInterceptor((config) => {
  config.headers = { ...config.headers, Authorization: "Bearer token" };
  return config;
});

const { data } = await api.get<User[]>("/users");
await api.post("/users", { name: "Ana" });
// Custom adapter (Adapter pattern)
import { HttpClient, type HttpAdapter } from "craft-kit";
const myAdapter: HttpAdapter = { async request(config) { /* use axios, etc. */ return /* HttpResponse */; } };
const client = new HttpClient({ adapter: myAdapter });

FetchAdapter uses globalThis.fetch by default (Node 18+ or the browser). On Node 16 pass it a fetch or use another adapter.

☕ Java-style utilities

import { Optional, Stream, Collectors, HashMap, TreeSet, AtomicInteger } from "craft-kit";

Optional.ofNullable(user).map((u) => u.name).orElse("guest");
Stream.range(1, 11).filter((n) => n % 2 === 0).map((n) => n * n).toArray();      // [4,16,36,64,100]
Stream.of(1, 2, 3, 4).collect(Collectors.groupingBy((n) => (n % 2 ? "odd" : "even")));
new HashMap<string, number>().put("a", 1);

Collections: ArrayList, LinkedList, HashMap, LinkedHashMap, TreeMap, HashSet, LinkedHashSet, TreeSet, MultiMap, MultiSet, BiMap, EnumMap, EnumSet. Plus AtomicInteger/AtomicLong/AtomicBoolean/AtomicReference.

Modern Java APIs (12 → 26)

import { Stream, Collectors, Gatherers, LazyConstant } from "craft-kit";

Stream.of(1, 2, 3).toList();                       // immutable array (Java 16)
Stream.of(1, 2, 3, 4).mapMulti((n, push) => {      // one-to-many without intermediate Streams
  if (n % 2 === 0) { push(n); push(n * 10); }
}).toArray();                                       // [2, 20, 4, 40]

// Gatherers: custom intermediate operations (JEP 485)
Stream.of(1, 2, 3, 4, 5, 6, 7).gather(Gatherers.windowFixed(3)).toList(); // [[1,2,3],[4,5,6],[7]]
Stream.of(1, 2, 3).gather(Gatherers.scan(() => 0, (a, b) => a + b)).toArray(); // [1, 3, 6]

// Collectors.teeing (Java 12): fork to two collectors and merge in one pass
Stream.of(10, 20, 30).collect(Collectors.teeing(
  Collectors.summing((x) => x),
  Collectors.counting(),
  (sum, count) => sum / count,
));                                                 // 20

// LazyConstant (JEP 526): compute-once, cached value
const config = LazyConstant.of(() => loadConfig());
config.get();                                       // computed only once

Also: SequencedCollection on ArrayList/LinkedList (addFirst/getFirst/removeLast/reversed()), atomics with compareAndExchange/accumulateAndGet, and Gatherers windowSliding/fold/distinctBy/limit. AsyncStream also supports mapMulti and gather (reusing the same Gatherers); for concurrent mapping (à la Gatherers.mapConcurrent) use AsyncStream.mapParallel.

🔁 Functional programming

import { Result, pipe, memoize, Lazy } from "craft-kit";

Result.ok(5).map((x) => x * 2).unwrapOr(0);          // 10
pipe((x: number) => x + 1, (x) => x * 2)(3);         // 8
const cached = memoize(expensiveFn);
const lazy = new Lazy(() => compute());              // evaluated only once

Also: Pair/Triple, Range, StringBuilder, compose, curry, debounce, throttle, once, tap, tryCatch.

⚡ Async

import { sleep, retry, timeout, pMap, Semaphore, RateLimiter, WorkerPool, AsyncStream } from "craft-kit";

await retry(() => fetchData(), { retries: 5, delayMs: 100, factor: 2 });   // backoff
await pMap(ids, (id) => loadUser(id), { concurrency: 4 });                  // parallel + ordered
const limiter = new RateLimiter({ tokensPerInterval: 5, intervalMs: 1000 });

await AsyncStream.from(ids).mapParallel((id) => fetchUser(id), 4).toArray();

Also: pAll, pSettle, Mutex, Deferred, AsyncQueue, and iter (lazy generators).

Resilience & concurrency: CircuitBreaker, Bulkhead, CountDownLatch, Channel (CSP), debounceAsync/throttleAsync.

import { CircuitBreaker } from "craft-kit";
const breaker = new CircuitBreaker({ failureThreshold: 5, resetTimeoutMs: 30000 });
await breaker.execute(() => callFlakyService());

🛠️ Utilities

Grouped into namespaces: arrays, numbers, booleans, strings, dates, objects, query, is, colors, units, csv, duration, ansi.

Binary: ByteBuffer, bytes, BitSet, bits (craft-kit/binary). Web Streams helpers: streams (craft-kit/streams).

Also: Money (decimal-safe), Random (seeded PRNG), uuidv7, otpauthURL, luhn/creditCardBrand + validators.isCreditCard/isIBAN/isPhone/isPostalCode, and ansi terminal colors.

import { arrays, strings, objects, is } from "craft-kit";

arrays.count([1, 2, 2, 3], 2);            // 2   (occurrences)
arrays.frequencies([1, 1, 2]);            // Map { 1 => 2, 2 => 1 }
arrays.isAllNumbers([1, 2, 3]);           // true (type guard)
arrays.unique([1, 1, 2]);                 // [1, 2]
arrays.groupBy(users, (u) => u.role);     // Map<role, User[]>

strings.count("banana", "a");             // 3
strings.charFrequency("aab");             // Map { a => 2, b => 1 }
strings.slugify("Canción Ñandú!");        // "cancion-nandu"

objects.deepClone(obj);                   // deep clone (also shallowClone)
objects.deepEqual(a, b);
objects.get(obj, "a.b[0].c", fallback);   // safe path access
objects.flatten({ a: { b: 1 } });         // { "a.b": 1 }

is.number(5);  is.array(x);  is.plainObject(x);  is.nullish(x);  is.empty(x);

numbers (clamp, round, formatThousands…), dates (format, parse, addDays, diffInDays, fromUnix/toUnix…), booleans (parse, xor…), query (parse/stringify query strings).

🛡️ Quick validation (null / empty)

Direct helpers (no namespace) for the checks you write all the time:

import { isNil, isEmpty, isBlank, coalesce, defaultTo, requireNonNull, ensureArray, parseNumber } from "craft-kit";

isNil(value);               // true if null or undefined
isEmpty(value);             // true: null, undefined, "", [], {}, empty Map/Set
isBlank("   ");             // true (empty or whitespace only)
coalesce(a, b, c);          // first non-null value (like ??, chainable)
defaultTo(value, fallback); // value, or fallback if null/undefined
requireNonNull(value);      // throws TypeError if null/undefined
ensureArray(x);             // x -> [x] ; [x] -> [x] ; null -> []
parseNumber("42", 0);       // 42  (or the fallback if not a valid number)

Flatten / expand objects and arrays:

import { objects, arrays } from "craft-kit";

objects.flatten({ a: { b: 1 }, c: [10, 20] });   // { "a.b": 1, "c.0": 10, "c.1": 20 }
objects.unflatten({ "a.b": 1, "c.0": 10 });      // { a: { b: 1 }, c: [10] }
arrays.flattenDeep([1, [2, [3, [4]]]]);          // [1, 2, 3, 4]

Also with type narrowing: is.number, is.string, is.array, is.plainObject, is.nullish, is.empty, etc.

🎨 Formatters & coercion

import { format, toInt, toDate, toBoolean, tryCatch, Result } from "craft-kit";

format.bytes(1536);                  // "1.5 KB"
format.duration(90000);              // "1m 30s"
format.relativeTime(date);           // "5 minutes ago" (locale-aware)
format.currency(1234.5);             // "$1,234.50"
format.percent(0.25);                // "25%"

toInt("42");                         // 42
toDate("2020-01-01");                // Date | undefined
toBoolean("yes");                    // true

// Capture errors as a Result, no try/catch:
const r = tryCatch(() => JSON.parse(input));
const value = r.isOk() ? r.unwrap() : fallback;
await Result.fromPromise(fetchData());          // Promise<Result<T, Error>>

More in the namespaces: objects.deepFreeze/invert/mapKeys/pickBy/omitBy, strings.pluralize/truncateMiddle/mask/escapeHtml. IDs: ulid(), base58Encode/base58Decode.

✅ Validation

import { isEmail, rut, isValidRut, validators, schema } from "craft-kit";

isEmail("[email protected]");                // true
rut.validate("12.345.678-5");              // true   (Chilean RUT)
rut.format("123456785");                   // "12.345.678-5"
rut.computeDv("12345678");                 // "5"
validators.isUrl("https://x.com");         // true

// Chainable schema validation (zod-style) -> parse / safeParse
const userSchema = schema.object({ name: schema.string().min(2), age: schema.number().int().min(0) });
userSchema.parse({ name: "Ana", age: 30 });
userSchema.safeParse(input);               // Result<User, SchemaError>

validators also: isUuid, isNumeric, isAlpha, isAlphanumeric, isInteger, isHexColor, isIpv4, isJson, isEmpty.

🔐 Security

import { hashPassword, verifyPassword, aesEncrypt, aesDecrypt, signJwt, verifyJwt, totp, nanoid } from "craft-kit";

const stored = hashPassword("s3cret");     // PBKDF2 with random salt
verifyPassword("s3cret", stored);          // true  (constant time)
const enc = aesEncrypt("secret", "key"); aesDecrypt(enc, "key");   // AES-256-GCM

const token = signJwt({ sub: "123" }, "secret", { expiresIn: 3600 });
verifyJwt(token, "secret");                // payload (throws on invalid/expired)

totp("BASE32SECRET");                      // 2FA code
nanoid();                                  // short URL-safe id

Also: sha256, sha512, md5, hmac, pbkdf2, generatePassword, passwordStrength, hotp, verifyTotp, base64/base32/hex, base58, constantTimeEqual, uuid, ulid, decodeJwt.

🧩 Extending

Algorithms use Strategy + Registry, so you can register your own:

import { Sorter } from "craft-kit";
const sorter = new Sorter();
sorter.register("mySort", (arr) => [...arr] /* your logic */);
sorter.sort("mySort", [3, 1, 2]);

📂 Subpath imports

Besides the root import, you can import per domain for cleaner imports and better tree-shaking:

import { HttpClient } from "craft-kit/http";
import { EventEmitter } from "craft-kit/patterns";
import { arrays, objects, is } from "craft-kit/utils";
import { Graph, dijkstra } from "craft-kit/algorithms";
import { Stack, LRUCache } from "craft-kit/structures";

Subpaths: craft-kit/{common, structures, algorithms, java, async, fp, patterns, http, binary, streams, utils, validation, security}.

✅ Compatibility

Node.js 16+ (the HTTP client uses fetch by default: Node 18+ or an adapter). ESM and CommonJS. Browser with any bundler. Types included.

🧪 Quality

Test suite with Vitest (npm test, npm run coverage) and benchmarks with tinybench (npm run bench). Dual ESM + CommonJS build with tsup and strict type-checking (npm run typecheck).

📚 Full reference

Every export, grouped by module, lives in FEATURES.md.

📜 License

MIT.