Core Exports
Install @chiballc/utils and import these values from the package root. This page covers every value and TypeScript type exported by that entry point. Unless a snippet shows a narrower import, its identifiers come from this root import.
import { execute, LRUCache, take } from "@chiballc/utils";Data helpers
Boolish
Boolish(value) normalizes string representations of simple values. It trims strings, returns true, false, null, or undefined for those literal strings, returns null for an empty string, and otherwise returns the trimmed string. Values that are not strings are returned unchanged.
Boolish(" true "); // true
Boolish(42); // 42hasOwnProperties
hasOwnProperties(object, properties, options) checks own properties rather than inherited ones. By default every requested property must exist. Set partial to accept any requested property, and set allowNullish to false when a present-but-falsy value should not count.
const profile = { name: "Ada", enabled: false };
hasOwnProperties(profile, ["name"]); // true
hasOwnProperties(profile, ["name", "missing"], { partial: true }); // trueisEmpty
isEmpty(value) returns true for nullish values, non-objects, and empty arrays, maps, sets, or objects. For objects, it checks own enumerable keys.
isEmpty({}); // true
isEmpty(new Set(["ready"])); // falsekeys
keys(value, warn?) is a generator that yields keys from objects, maps, sets, and arrays. For a string it yields the string itself. It refuses generators because consuming them for keys is ambiguous, and can warn when given an unsupported primitive.
const getKeys: KeysFunc = keys;
[...getKeys({ theme: "dark" })]; // ["theme"]entries
entries(value, warn?) is a generator that yields [key, value] pairs. Objects use property keys, arrays use numeric indexes, maps and sets use their native entries, and iterable or generator values are indexed from zero. Asynchronous inputs produce an asynchronous generator.
[...entries(["a", "b"])]; // [[0, "a"], [1, "b"]]
async function* stream() {
yield "event";
}
for await (const entry of entries(stream())) console.log(entry);values
values(value, warn?) is the value-only counterpart to entries. It preserves map and set iteration semantics, indexes iterable and generator inputs internally, and becomes an asynchronous generator for asynchronous sources.
const getValues: ValuesFunc = values;
[...getValues(new Set(["a", "b"]))]; // ["a", "b"]KeysFunc
KeysFunc is the TypeScript call signature implemented by keys. It describes a generator of the input's key type and accepts the optional warning flag.
const getKeys: KeysFunc = keys;
const objectKeys = [...getKeys({ id: 1 })]; // string[]EntriesFunc
EntriesFunc is the TypeScript call signature implemented by entries. It selects a synchronous or asynchronous generator based on the supplied source.
const getEntries: EntriesFunc = entries;
const pairs = [...getEntries({ id: 1 })]; // [["id", 1]]ValuesFunc
ValuesFunc is the TypeScript call signature implemented by values. It models the generator type and value type inferred from the supplied source.
const getValues: ValuesFunc = values;
const items = [...getValues({ id: 1 })]; // [1]Peek
Peek is the TypeScript overload interface for peek. It preserves the special return types for arrays, maps, sets, synchronous generators, and asynchronous generators.
const first: Peek = peek;
const item = first(["first", "second"]); // string | undefinedisAsyncIterable
isAsyncIterable(value) is a type guard for values with Symbol.asyncIterator. Use it before for await...of when the input type is not known.
if (isAsyncIterable<string>(source)) {
for await (const value of source) console.log(value);
}omit
omit(object, key) copies an object and removes one key or a list of keys. Dotted string paths remove nested properties from the copied result; the input object is not modified.
omit({ id: 1, secret: "x", user: { email: "a@b.test" } }, ["secret", "user.email"]);
// { id: 1, user: {} }pick
pick(object, key) copies selected keys into a new object. Dotted paths create the matching nested result. When given exactly one dotted path, it returns that nested value directly.
pick({ id: 1, user: { name: "Ada" } }, ["id", "user.name"]);
pick({ user: { name: "Ada" } }, "user.name"); // "Ada"Execution helpers
isPromise
isPromise(value) checks for a truthy value with a then function. It is a lightweight thenable guard rather than a strict Promise constructor check.
isPromise(Promise.resolve("ready")); // true
isPromise({ then() {} }); // trueexecute
execute(valueOrFunctionOrPromise, ...args) returns { result, error }. It invokes functions with the supplied arguments, awaits promises, and captures thrown or rejected errors instead of throwing them from the immediate call site.
execute(42); // { result: 42, error: undefined }
execute((name: string) => name.toUpperCase(), "ada");
await execute(Promise.resolve({ ok: true }));settle
settle(promises, onError?) waits for all supplied promises and returns an array in the original order. Fulfilled positions contain their values; rejected positions contain undefined and are passed to onError when it is supplied.
const [profile, settings] = await settle([getProfile(), getSettings()], (index, error) => {
console.warn(`Request ${index} failed`, error.reason);
});race
race(promises) resolves or rejects with the first settled promise. It rejects an empty input and falls back to an internal race implementation only on platforms without Promise.race.
const response = await race([fetch("/primary"), fetch("/replica")]);makeThenable
makeThenable(source, promise) returns a proxy that exposes the source object's properties and the promise's methods. This is useful when an object needs both an immediate API and awaitable completion.
const task = makeThenable({ id: "sync-id" }, Promise.resolve("complete"));
task.id; // "sync-id"
await task; // "complete"Generator helpers
isGenerator
isGenerator(value) is a type guard for synchronous generator instances. It verifies the generator methods and generator identity without advancing the iterator.
function* pages() {
yield 1;
}
isGenerator(pages()); // trueisAsyncGenerator
isAsyncGenerator(value) is the asynchronous equivalent of isGenerator. It does not call next, so it does not consume a value while checking.
async function* pages() {
yield 1;
}
isAsyncGenerator(pages()); // truemapOverGenerator
mapOverGenerator(generator, callback, options?) calls callback for every yielded value and its zero-based index. It returns void for synchronous generators and a promise for asynchronous generators. options.onReturn receives the generator's final return value.
function* ids() {
yield "a";
return "done";
}
mapOverGenerator(ids(), (id, index) => console.log(index, id), {
onReturn: (result) => console.log(result),
});collectFromGenerator
collectFromGenerator(generator) consumes a generator into an array. It returns an array for synchronous generators and a promise of an array for asynchronous generators.
function* ids() {
yield "a";
yield "b";
}
collectFromGenerator(ids()); // ["a", "b"]joinGenerators
joinGenerators(...sources) combines synchronous generators, asynchronous generators, and functions that produce either kind. It always returns an asynchronous generator and yields each source completely before moving to the next one.
function* local() {
yield "local";
}
async function* remote() {
yield "remote";
}
for await (const value of joinGenerators(local(), remote)) console.log(value);Collection helpers
peek
peek(value) returns the first array item, map value, set value, iterable item, object value, or the value itself when it is not a collection. For generators it reads the first result and patches the generator so the next next() call returns that same result.
peek(["first", "second"]); // "first"
peek(new Map([["id", 42]])); // 42
peek({ status: "ready" }); // "ready"take
take(value, count, start?) returns up to count values from an array, map, set, iterable, generator, object, or scalar. start defaults to zero. It warns before consuming a generator or iterator because that advances a non-reusable source.
take(["a", "b", "c"], 2); // ["a", "b"]
take(new Set(["a", "b", "c"]), 1, 1); // ["b"]
take({ first: "a", second: "b" }, 1); // ["a"]toArray
toArray(value) materializes maps, sets, generators, and other iterables into arrays. A string becomes a one-element array rather than a character array; a scalar also becomes a one-element array; nullish values become an empty array.
toArray(new Map([["id", 42]])); // [["id", 42]]
toArray("one"); // ["one"]
toArray(null); // []isIterable
isIterable(value) is a type guard for values with Symbol.iterator. Strings are iterable, even though some collection helpers intentionally handle them as scalar values.
isIterable([1, 2]); // true
isIterable("text"); // true
isIterable({}); // falseisFunction
isFunction(value) is a type guard for JavaScript functions.
isFunction(() => {}); // true
isFunction({ call() {} }); // falseisNone
isNone(value) is a type guard that returns true only for null and undefined.
isNone(undefined); // true
isNone(null); // true
isNone(0); // falseNumber and locale helpers
toNumber
toNumber(value) converts a value with Number, then falls back to parseInt. It logs and returns 0 when conversion cannot produce a number.
toNumber("42"); // 42
toNumber("not-a-number"); // 0toFloat
toFloat(value) converts a value with Number, then falls back to parseFloat. It logs and returns 0 when conversion fails.
toFloat("3.14"); // 3.14
toFloat("not-a-number"); // 0toLocaleNumber
toLocaleNumber(value, locale?) converts with toNumber and formats with zero minimum and maximum fraction digits.
toLocaleNumber(12345.67, "en-US"); // "12,346"toLocaleFloat
toLocaleFloat(value, locale?) converts with toFloat and formats with exactly two minimum and maximum fraction digits.
toLocaleFloat(12345.6, "en-US"); // "12,345.60"Timing helpers
debounce
debounce(function, delay?) returns a function that waits until calls stop before invoking the original function. The default delay is 200 milliseconds, and the wrapper returns the most recent completed result.
const saveDraft = debounce((text: string) => api.save(text), 300);
saveDraft("first");
saveDraft("latest"); // only this call runs after 300 msthrottle
throttle(function, limit) returns a function that invokes the original at most once during each limit millisecond window. Calls inside the window return the last result.
const reportScroll = throttle(() => analytics.track("scroll"), 500);
window.addEventListener("scroll", reportScroll);sleep
sleep(milliseconds) returns a promise that resolves after the supplied delay.
await sleep(250);toMilliSeconds
toMilliSeconds(value, unit) converts "hr", "min", or "s" to milliseconds. Passing "ms" is not supported and throws.
toMilliSeconds(2, "hr"); // 7_200_000
toMilliSeconds(5, "min"); // 300_000
toMilliSeconds(30, "s"); // 30_000toSeconds
toSeconds(value, unit) converts "hr", "min", or "s" to seconds. Passing "ms" is not supported and throws.
toSeconds(2, "hr"); // 7_200
toSeconds(5, "min"); // 300
toSeconds(30, "s"); // 30toHours
toHours(value, unit) converts "min", "s", or "ms" to hours.
toHours(120, "min"); // 2
toHours(7_200, "s"); // 2
toHours(7_200_000, "ms"); // 2Structures and algorithms
Queue
Queue<T> is a FIFO queue. Create it with new Queue<T>(), add values with enqueue, remove the oldest value with dequeue, and read the current number of values through length. dequeue returns null when empty.
const jobs = new Queue<string>();
jobs.enqueue("email");
jobs.enqueue("report");
jobs.dequeue(); // "email"
jobs.length; // 1Cache
Cache<K, V> is the abstract cache contract. It defines get, set, has, delete, clear, and size for implementations such as the two LRU caches.
const cache: Cache<string, number> = new LRUCache(10);
cache.set("attempts", 1);LRUCache
LRUCache<K, V> keeps a bounded number of recent values. get refreshes a value's recency, and set evicts the least recently used value when capacity is reached. Its constructor rejects a non-positive maximum size.
const cache = new LRUCache<string, number>(2);
cache.set("a", 1);
cache.set("b", 2);
cache.get("a"); // refreshes "a"
cache.set("c", 3); // evicts "b"LRUTimeCache
LRUTimeCache<K, V> adds a TTL to LRUCache. set defaults to a 60-second lifetime, get removes expired values, has respects expiry, and update replaces an existing value while keeping its TTL unless a new one is supplied.
const cache = new LRUTimeCache<string, string>(10);
cache.set("status", "ready", 5_000);
cache.update("status", "complete");
cache.get("status"); // "complete" until expirybinarySearch
binarySearch(sortedArray, target) returns the target index or -1. For complex values, pass a comparator that receives an array item and one of "<", "=", or ">" and reports its relationship to the desired target.
binarySearch([2, 4, 6, 8], 6); // 2
binarySearch([{ id: 1 }, { id: 3 }], (item, op) =>
op === "=" ? item.id === 3 : op === "<" ? item.id < 3 : item.id > 3,
); // 1ValidationError
ValidationError is an Error subclass for failures that callers should identify as validation problems.
if (!email.includes("@")) throw new ValidationError("A valid email is required");Environment and identifiers
isVercel
isVercel is evaluated at module load and is true when VERCEL or NOW_REGION exists in the process environment.
if (isVercel) enableDeploymentTelemetry();isDevelopment
isDevelopment is evaluated at module load from ENV or NODE_ENV. It is true for development and dev values, including case-insensitive string forms.
if (isDevelopment) enableVerboseLogs();isProduction
isProduction is the inverse of isDevelopment.
if (isProduction) enableProductionOnlyCaching();isWebWorker
isWebWorker is true when the current global scope is a WorkerGlobalScope.
if (isWebWorker) self.postMessage("worker ready");isServiceWorker
isServiceWorker is true when the current global scope is a ServiceWorkerGlobalScope.
if (isServiceWorker) self.skipWaiting();isWorker
isWorker is true when either isWebWorker or isServiceWorker is true.
if (isWorker) console.log("running outside the window context");kinoid
kinoid() creates a stateful ID generator with newId() and decodeId(id). newId creates a 17-character, base-36 identifier containing a time offset, sequence number, and process ID. decodeId returns those decoded fields or an error object. The identifiers are sortable and unique for the generator process, but are not cryptographic secrets.
const ids = kinoid();
const id = ids.newId();
const decoded = ids.decodeId(id);