
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Some utils and remix that I repeated in many projects.
This package includes some light-weight alternatives to packages like:
| our | is alternative to / remix of |
|---|---|
| elt / clsx | clsx, classnames, h, hyperscript |
| maybeAsync / makePromise / PromiseEx | imperative-promise, bluebird |
| stringHash | cyrb53, murmurhash ... |
| randomStr | nanoid |
| <some lodash-like functions> | lodash |
There are also some interesting original utils like shallowEqual / newFunction / toArray / getVariableName etc. Feel free to explore!
Other cool third-party libraries:
All modules are shipped as ES modules and tree-shakable.
via package manager
npm install yon-utils
via import within <script type="module">
import { elt } from "https://unpkg.com/yon-utils"
convertor/json-schemajsonSchemaToTypeScript(schema, indent?)schema: JSONSchema
indent?: number
returns: string
a simple util to translate JSON Schema to TypeScript type declaration, with shortest comment (single line if possible)
dom/clsxclsx(...args)...args: any[]
returns: string
construct className strings conditionally.
can be an alternative to classnames(). modified from lukeed/clsx. to integrate with Tailwind VSCode, read this
dom/eltelt(tagName, attrs, ...children)tagName: string โ e.g. "button" or "button.my-btn#myId"
attrs: any โ class (processed by clsx), style (string/object), onClick (auto addEventListener)
...children: any[] โ strings, numbers, nodes
returns: HTMLElement
Simplified document.createElement with support for class/id shortcuts, event listeners, and JSX.
Works with JSX: /** @jsx elt */
onClick_capture / onClick_once / onClick_self
className is alias of class, supports { 'my-button': true, 'is-primary': XXX }elt('button.myButton', { onclick: () => alert('hi') }, 'Click Me!') Click Me
execute/functionnewFunction(argumentNames, functionBody, options?)argumentNames: NameArray<ARGS> โ a string[] of argument names
functionBody: string โ the function body
options?: { async? }
boolean | undefined โ set to true if the code contains await, the new function will be an async functionreturns: Fn<RESULT, ARGS>
like new Function but with more reasonable options and api
noop()voidThe nothing-to-do function
execute/retryretry(fn, opts?)fn: () => T | Nil | Promise<T | Nil> โ Function to retry
opts?: { duration?, interval?, signal? }
duration?: number | undefined
interval?: number | undefined
signal?: AbortSignal | undefined
returns: Promise<T> โ Promise resolving to the first successful result
Retries fn() until it returns a non-null value without throwing.
Throws when timeout or aborted.
pollUntil(fn, opts?)fn: () => T | Nil | Promise<T | Nil> โ - Function to retry
opts?: { duration?, interval?, signal? }
duration?: number | undefined
interval?: number | undefined
signal?: AbortSignal | undefined
returns: Promise<T>
Alias for retry. Polls a function until it returns a non-null value, or throws if timeout or aborted.
flow/fnQueuefnQueue()returns: { tap, tapSilent, call, queue }
tap: AddCallbacks<Args> โ add one or more functions.
tapSilent: AddCallbacks<Args> โ add functions, and will silently ignore their errors
call: (...args: Args) => void โ run functions. if fnQueue is async, returns Promise
queue: { silent?: boolean | undefined; fn: Fn<any, Args>; }[] โ the queued functions
Store a list of functions, and execute them in order.
Use decorators or options, to customize a fnQueue:
fnQueue.async() to create async queue -- the call() will return a Promise instead.fnQueue.filo() to create FILO queue.fnQueue.onetime() to clear the queue after each call.fnQueue({ error: 'ignore' }) to ignore errors.Options can be combined, like fnQueue.async.onetime() -- see example below.
// create an async fnQueue with options ...
const disposer = fnQueue.async.onetime({ error: 'ignore' });
try {
const srcFile = await openFile(path1);
disposer.tap(() => srcFile.close());
const dstFile = await openFile(path2);
disposer.tap(() => dstFile.close());
await copyData(srcFile, dstFile);
} finally {
await disposer.call();
}
async: Factory<Promise<void>>
async: Factory<Promise<void>>
filo: Factory<Promise<void>> โ change execution order to FILO (first-in, last-out, like a stack)
onetime: Factory<Promise<void>> โ after each call, clear the queue
filo: Factory<void> โ change execution order to FILO (first-in, last-out, like a stack)
async: Factory<Promise<void>>
filo: Factory<void> โ change execution order to FILO (first-in, last-out, like a stack)
onetime: Factory<void> โ after each call, clear the queue
onetime: Factory<void> โ after each call, clear the queue
async: Factory<Promise<void>>
filo: Factory<void> โ change execution order to FILO (first-in, last-out, like a stack)
onetime: Factory<void> โ after each call, clear the queue
flow/makeAsyncIteratormakeAsyncIterator()returns: { write(value: T): void; end(error?: any): void; } & AsyncIterableIterator<T>
write: (value: T) => void
end: (error?: any) => void
Help you convert a callback-style stream into an async iterator. Also works on "observable" value like RxJS.
You can think of this as a simplified new Readable({ ... }) without headache.
const iterator = makeAsyncIterator();
socket.on('data', value => iterator.write(value));
socket.on('end', () => iterator.end());
socket.on('error', (err) => iterator.end(err));
for await (const line of iterator) {
console.log(line);
}
flow/makeEffectmakeEffect(fn, isEqual?)fn: (input: T, previous: T | undefined) => void | (() => void)
isEqual?: (x: T, y: T) => boolean
returns: { cleanup, value }
cleanup: () => void โ invoke last cleanup function, and reset value to undefined
value?: T | undefined โ get last received value, or undefined if effect was clean up
Wrap fn and create an unary function. The actual fn() executes only when the argument changes.
Meanwhile, your fn may return a cleanup function, which will be invoked before new fn() calls
-- just like React's useEffect
The new unary function also provide cleanup() method to forcedly do the cleanup, which will also clean the memory of last input.
const sayHi = makeEffect((name) => {
console.log(`Hello, ${name}`);
return () => {
console.log(`Goodbye, ${name}`);
}
});
sayHi('Alice'); // output: Hello, Alice
sayHi('Alice'); // no output
sayHi('Bob'); // output: Goodbye, Alice Hello, Bob
sayHi.cleanup(); // output: Goodbye, Bob
sayHi.cleanup(); // no output
flow/withDeferwithDefer(fn)fn: (defer: AddCallbacks<[]>) => Ret
returns: Ret
Get rid of try catch finally hells!
Use defer(callback) to clean up resources, and they will run in finally stage.
Works on both sync and async procedures.
For sync functions:
// sync
const result = withDefer((defer) => {
const file = openFileSync('xxx')
defer(() => closeFileSync(file)) // <- register callback
const parser = createParser()
defer(() => parser.dispose()) // <- register callback
return parser.parse(file.readSync())
})
For async functions, use withAsyncDefer
// async
const result = await withAsyncDefer(async (defer) => {
const file = await openFile('xxx')
defer(async () => await closeFile(file)) // <- defer function can be async now!
const parser = createParser()
defer(() => parser.dispose()) // <-
return parser.parse(await file.read())
})
Error handling
If one callback throws, rest callbacks still work. And you get the last error thrown.
To suppress a callback's throwing, use defer.silent(callback)
defer.silent(() => closeFile(file)) // will never throws
Refer to TypeScript using syntax,
TC39 Explicit Resource Management and GoLang's defer keyword.
withAsyncDefer(fn)fn: (defer: AddCallbacks<[]>) => Ret
returns: Ret
Same as withDefer, but this returns a Promise, and supports async callbacks.
flow/worker-rpccreateWorkerHandler(methods)methods: T โ Object containing method implementations that can be called remotely
returns: (payload: RpcPayload) => void โ A handler function that processes incoming RPC payloads
Creates a handler function for processing RPC requests in a Worker.
// In your worker file:
import { createWorkerHandler } from './worker-rpc';
const handler = createWorkerHandler({
async getData(id) {
// implementation
return result;
}
});
self.onmessage = (e) => {
if (e.data?.type === 'myCall') handler(e.data.payload);
}
createWorkerDispatcher(postMessage)postMessage: (payload: RpcPayload, transferable: Transferable[]) => void โ Function that sends messages to the worker (typically worker.postMessage)
returns: T โ A proxy object where each property access creates a function that calls the corresponding method in the worker
Creates a proxy object that dispatches method calls to a Worker via RPC.
// In your main thread:
import { createWorkerDispatcher } from './worker-rpc';
const worker = new Worker('path/to/worker.js');
const api = createWorkerDispatcher<MyWorkerAPI>((payload, transferable) =>
worker.postMessage({
type: 'myCall',
payload,
}, transferable)
);
// Now you can call worker methods as if they were local:
// No need to worry about initiating, it automatically wait for the worker to be ready
const result = await api.getData(123);
interaction/clipboardwriteClipboard(text)text: string
returns: Promise<void>
write text to clipboard, with support for insecure context and legacy browser!
note: if you are in HTTPS and modern browser, you can directly use navigator.clipboard.writeText() instead.
readClipboard(timeout?)timeout?: number โ default 1500
returns: Promise<string>
read clipboard text.
if user rejects or hesitates about the permission for too long, this will throw an Error.
interaction/keyboardmodKey(ev)ev: KeyboardEventLike
returns: number
get Modifier Key status from a Event
modKey.Mod to indicate if the key is โ(Cmd) on Mac, or Ctrl on Windows/Linux| (or operator) to combine modifier keys. see example below.if (modKey(ev) === (modKey.Mod | modKey.Shift) && ev.code === 'KeyW') {
// Ctrl/Cmd + Shift + W, depends on the OS
}
None: 0
Ctrl: 1
Cmd: 2
Shift: 4
Alt: 8
Mod: 1 | 2 โ equals to Ctrl or Cmd, depending on the OS
IS_MACboolean
MOD_KEYthe proper way to get modKey status from KeyboardEvent
"metaKey" | "ctrlKey"
MOD_KEY_LABELthe proper label of modKey. eg: โ for Mac, Ctrl for Windows/Linux
"โ" | "Ctrl"
interface KeyboardEventLikectrlKey?: boolean | undefined
metaKey?: boolean | undefined
shiftKey?: boolean | undefined
altKey?: boolean | undefined
interaction/mouseMovestartMouseMove({ initialEvent, onMove, onEnd })__0: MouseMoveInitOptions
returns: Promise<MouseMoveInfo> โ a Promise with final position when user releases button
Powerful tool to implement any drag / resize action easily!
While dragging, it tracks the cursor's movement and reports to onMove(...).
And when user releases the button, it will call onEnd(...).
the element needs touch-action: none style to prevent scrolling on mobile
use within pointerdown event
event.preventDefault() is necessary too
button.style.touchAction = 'none' // CSS touch-action: none
button.addEventListener('pointerdown', event => {
event.preventDefault();
startMouseMove({
initialEvent: event,
onMove({ deltaX, deltaY }) { ... },
onEnd({ deltaX, deltaY }) { ... },
});
});
interface MouseMoveInfoclientX: number
clientY: number
deltaX: number
deltaY: number
duration: number โ in milliseconds, since startMouseMove called
event: MouseEvent | PointerEvent
pointerId: number | false
cancelled?: boolean | undefined
interface MouseMoveInitOptionsinitialEvent: MouseEvent | PointerEvent
onMove?: ((data: MouseMoveInfo) => void) | undefined
onEnd?: ((data: MouseMoveInfo) => void) | undefined
iterable/iterabletoArray(value)value?: OneOrMany<T>
returns: NonNullable<T>[]
Input anything, always return an array.
Array.from() to process it.Finally before returning, all null and undefined will be omitted
find(iterator, predicate)iterator?: Nil | Iterable<T>
predicate: Predicate<T>
returns: T | undefined
Like Array#find, but the input could be a Iterator (for example, from generator, Set or Map)
findMap(iterator, map)iterator?: Nil | Iterable<Nil | T>
map: PredicateAndMap<T, U>
returns: U | undefined
Find the first item that matches the predicate, and then map it to a new value.
map function must return non-null value, otherwise it will be skipped.
mapFilter(iterator, map)iterator?: Nil | Iterable<Nil | T>
map: PredicateAndMap<T, U>
returns: U[]
Type-safe Shortcut for .map(fn).filter(val => !isNil(val))
map function must return non-null value, otherwise it will be skipped.
reduce(iterator, initial, reducer)iterator?: Nil | Iterable<T>
initial: U
reducer: (agg: U, item: T, index: number) => U
returns: U
Like Array#reduce, but the input could be a Iterator (for example, from generator, Set or Map)
head(iterator)iterator?: Nil | Iterable<T>
returns: T | undefined
Take the first result from a Iterator
contains(collection, item)collection?: Nil | CollectionOf<T>
item: T
returns: boolean
input an array / Set / Map / WeakSet / WeakMap / object etc, check if it contains the item
forEach(objOrArray, iter)objOrArray: U
iter: (value: U[keyof U], key: keyof U, whole: U) => void
returns: void
a simple forEach iterator that support both Array | Set | Map | Object | Iterable as the input
when met plain object, it works like forIn of lodash.
type OneOrMany<T>export type OneOrMany<T> = Iterable<T | Nil> | T | Nil
type Predicate<T>export type Predicate<T> = (value: T, index: number) => boolean;
type PredicateAndMap<T, U>export type PredicateAndMap<T, U> = (value: T, index: number) => U | Nil;
filterMap(iterator, map)iterator?: Nil | Iterable<Nil | T>
map: PredicateAndMap<T, U>
returns: U[]
alias of mapFilter
type CollectionOf<T>export type CollectionOf<T> =
| ReadonlyArray<T>
| Set<T> | Map<T, any>
| (T extends object ? WeakMap<T, any> | WeakSet<T> : never)
| (T extends string ? Record<T, any> : never)
type IterItem<T>export type IterItem<T> = T extends Iterable<infer U> ? U : never;
manager/moduleLoadernew ModuleLoader<T>(source)ModuleLoaderSource<T>All-in-one ModuleLoader, support both sync and async mode, can handle circular dependency problem.
const loader = new ModuleLoader({
// sync example
resolve(query, { load }) {
if (query === 'father') return 'John'
if (query === 'mother') return 'Mary'
// simple alias: just `return load('xxx')`
if (query === 'mom') return load('mother')
// load dependency
// - `load('xxx').value` for sync, don't forget .value
// - `await load('xxx')` for async
if (query === 'family') return `${load('father').value} and ${load('mother').value}`
// always return something as fallback
return 'bad query'
}
})
console.log(loader.load('family').value) // don't forget .value
const loader = new ModuleLoader({
// async example
async resolve(query, { load }) {
if (query === 'father') return 'John'
if (query === 'mother') return 'Mary'
// simple alias: just `return load('xxx')`
if (query === 'mom') return load('mother')
// load dependency
// - `await load('xxx')` for async
// - no need `.value` in async mode
if (query === 'family') return `${await load('father')} and ${await load('mother')}`
// always return something as fallback
return 'bad query'
}
})
console.log(await loader.load('family')) // no need `.value` with `await`
ModuleLoader ยป
ModuleLoader # cacheModuleLoaderCache<{ dependencies?: string[] | undefined; promise: PromiseEx<T>; }>ModuleLoader # load(query)query: string
returns: PromiseEx<T>
fetch a module
ModuleLoader # getDependencies(query, deep?)query: string
deep?: boolean
returns: PromiseEx<string[]>
get all direct dependencies of a module.
note: to get reliable result, this will completely load the module and deep dependencies.
new CircularDependencyError(query, queryStack)query: string
queryStack: string[]
The circular dependency Error that ModuleLoader might throw.
CircularDependencyError ยป
CircularDependencyError # querystringthe module that trying to be loaded.
CircularDependencyError # queryStackstring[]the stack to traceback the loading progress.
CircularDependencyError # namestringalways 'CircularDependencyError'
type ModuleLoaderCache<T>used by ModuleLoader
get: (query: string) => T | undefined
set: (query: string, value: T) => any
delete: (query: string) => any
clear: () => void
interface ModuleLoaderSource<T>used by ModuleLoader
resolve: (query: string, ctx: { load(target: string): PromiseEx<T>; noCache<T>(value: T): T; }) => MaybePromise<T> โ You must implement a loader function. It parse query and returns the module content.
load() from ctx to load dependencies. Example: await load("common") or load("common").valuectx.noCache. Example: return noCache("404: not found")cache?: ModuleLoaderCache<any> | undefined
manager/objectKeygetObjectUniqueKey(obj)obj: any โ The object or function to get a unique key for
returns: number | undefined โ A unique numeric identifier, or undefined if input invalid
Gets a unique numeric key for an object. (value >= 1)
Uses a global WeakMap store to ensure the same object always returns the same key across calls.
Create a new isolated store with getObjectUniqueKey.createStore(onCreate?)
onCreate(obj, key) - optional, called when a new key is created.const obj = {};
getObjectUniqueKey(obj); // 1
getObjectUniqueKey(obj); // 1 (same key)
getObjectUniqueKey({}); // 2 (different object)
<T = any>(onCreate?: ((obj: T, key: number) => void) | undefined) => ObjectKeyGettermath/geometryisInsideRect(x, y, rect)x: number โ The x-coordinate of the point.
y: number โ The y-coordinate of the point.
rect: RectLike โ The rectangle to check against.
returns: boolean
Determines whether a point (x, y) is inside a rectangle.
isRectEqual(rect1, rect2, epsilon?)rect1?: Nil | RectLike โ The first rectangle to compare.
rect2?: Nil | RectLike โ The second rectangle to compare.
epsilon?: number | undefined โ The maximum difference allowed between the values of the rectangles' properties.
returns: boolean
Determines whether two rectangles are equal.
getRectIntersection(...rects)...rects: (Nil | RectLike)[] โ The rectangles
returns: RectLike โ The intersection rectangle. Can be passed to DOMRect.fromRect(.)
Calculates the intersection of rectangles.
getRectUnion(...rects)...rects: (Nil | RectLike)[] โ The rectangles
returns: RectLike โ The union rectangle. Can be passed to DOMRect.fromRect(.)
Calculates the union (out bounding box) of rectangles.
type RectLikea interface that fits DOMRect and many other situation
export type RectLike = RectLikeXYWH | RectLikeLTWH;
interface RectLikeXYWHx: number
y: number
width: number
height: number
interface RectLikeLTWHleft: number
top: number
width: number
height: number
math/numberapprox(a, b, epsilon?)a: number
b: number
epsilon?: number โ The maximum difference allowed between the two numbers. Defaults to 0.001.
returns: boolean
Determines if two numbers are approximately equal within a given epsilon.
clamp(n, min, max)n: number
min: number
max: number
returns: number
Clamp a number between min and max
lerp(min, max, t)min: number
max: number
t: number โ The interpolation value clamped between 0 and 1
returns: number
Linearly interpolate
const value = lerp(0, 2, 0.5) // value will be 1
easeOut(t)t: number
returns: number
simple ease-out function, useful for animation, tween, and fake progress bar
// a fake progress bar never reach 100%
const sinceTime = Date.now()
const progress = 0.95 * easeOut((Date.now() - sinceTime) / 5000)
randomNum(min, max)min: number
max: number
returns: number
Get a random number from [min, max)
promise/miscdelay(milliseconds)milliseconds: number
returns: Promise<void>
debouncePromise(fn)fn: () => Promise<T> โ The function to be debounced.
returns: { (): Promise<T>; clear(): void; } โ The debounced function.
() => void โ make next call always return new PromiseCreates a debounced version of a function that returns a promise.
The returned function will ensure that only one Promise is created and executed at a time, even if the debounced function is called multiple times before last Promise gets finished.
All suppressed calls will get the last started Promise.
use lodash's throttle() if you want to support things like trailing and cache-timeout (aka. interval) option.
promise/promisemaybeAsync(input)input: T | Promise<T> | (() => T | Promise<T>) โ your sync/async function to run, or just a value
returns: PromiseEx<Awaited<T>> โ a crafted Promise that exposes { status, value, reason }, whose status could be "pending" | "fulfilled" | "rejected"
Run the function, return a crafted Promise that exposes status, value and reason
If input is sync function, its result will be stored in promise.value and promise.status will immediately be set as "fulfilled"
Useful when you are not sure whether fn is async or not.
makePromise()ImperativePromiseEx<T>Create an imperative Promise.
Returns a Promise with these 2 methods exposed, so you can control its behavior:
.resolve(result).reject(error)Besides, the returned Promise will expose these useful properties so you can get its status easily:
.wait([timeout]) โ wait for result, if timeout set and exceeded, a PromisePendingError will be thrown.status โ could be "pending" | "fulfilled" | "rejected".result and .reason.value โ fail-safe get result (or cause an Error from rejection, or cause a PromisePendingError if still pending)const handler = makePromise();
doSomeRequest(..., result => handler.resolve(result));
// wait with timeout
const result = await handler.wait(1000);
// or just await
const result = await handler;
new PromiseEx<T>(executor)(resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => voida crafted Promise that exposes { status, value, reason }
Note: please use maybeAsync() or PromiseEx.resolve() to create a PromiseEx
PromiseEx ยป
PromiseEx # status"pending" | "fulfilled" | "rejected"PromiseEx # reasonanyif rejected, get the reason.
PromiseEx # resultT | undefinedget result, or nothing if not fulfilled.
note: you might need .value which follows fail-fast mentality
PromiseEx # loadingbooleanequivalent to .status === "pending"
PromiseEx # isPending()booleanPromiseEx # isFulfilled()booleanPromiseEx # isRejected()booleanPromiseEx # valueT | undefinedfail-fast mentality, safely get the result.
new PromisePendingError(this).reason.resultPromiseEx # wait(timeout?)timeout?: number | undefined
returns: Promise<T>
wait for resolved / rejected.
optionally can set a timeout in milliseconds. if timeout, a PromisePendingError will be thrown
PromiseEx # thenImmediately(onfulfilled?, onrejected?)onfulfilled?: Nil | ((value: T) => TResult1 | PromiseLike<TResult1>)
onrejected?: Nil | ((reason: any) => TResult2 | PromiseLike<TResult2>)
returns: PromiseEx<TResult1 | TResult2>
Like then() but immediately invoke callbacks, if this PromiseEx
is already resolved / rejected.
resolve: { (): PromiseEx<void>; <T>(input: T): PromiseEx<Awaited<T>>; } โ Creates a new resolved promise.
Creates a new resolved promise for the provided value.
reject: <T = never>(reason?: any) => PromiseEx<T> โ Creates a new rejected promise for the provided reason.
new PromisePendingError(cause)Promise<any>Could be thrown from .value and .wait(timeout) of PromiseEx
PromisePendingError ยป
PromisePendingError # causePromise<any>type ImperativePromiseEx<T>export type ImperativePromiseEx<T> = PromiseEx<Awaited<T>> & {
resolve(result: T | PromiseLike<T>): void
reject(reason?: any): void
}
promise/swappablePromiseinterface SwappablePromiseExecutorsignal: AbortSignal โ triggered when current async process is replaced by another run()
isCurrent: () => boolean โ whether current process is the newest pending Promise. will become false when resolved / rejected / "replaced by other Promise"
throwIfNotCurrent: () => void โ throw if current process is not the newest pending Promise
new SwappablePromise<T>()A swappable promise that allows dynamically changing the underlying promise being waited upon.
Useful in avoiding race conditions for UI updates.
This "SwappablePromise" will wait for the newest promise to resolve.
You can use run(fn) or swap(promise) to change the underlying promise.
example use cases:
run() to get context and the signal)SwappablePromise ยป
SwappablePromise # wait(timeout?)timeout?: number | undefined
returns: Promise<T>
wait for the underlying Promise resolved. the waiting target may be swapped, before resolved or rejected.
if no underlying Promise yet, will keep waiting.
SwappablePromise # run(executor)executor: (ctx: SwappablePromiseExecutor) => T | Promise<T>
returns: void
swap to a new async process, and pivot wait() & then() to wait for it
SwappablePromise # swap(promise)promise: T | Promise<T>
returns: void
swap to a new Promise. just alias of run(() => promise)
SwappablePromise # reset()voidreset to "pending" status
if someone is waiting, it will keep waiting, NOT aborted.
SwappablePromise # hasTarget()booleanwhether a underlying Promise is set and pending
SwappablePromise # isPending()booleanwhether is pending
SwappablePromise # isFulfilled()booleanwhether is fulfilled
SwappablePromise # isRejected()booleanwhether is rejected
SwappablePromise # then(onfulfilled?, onrejected?)onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined
returns: Promise<TResult1 | TResult2>
wait for the underlying promise to resolve. the waiting target may be swapped, before resolved or rejected.
if no underlying Promise yet, will keep waiting.
string/stringstringHash(str)str: string
returns: number
Quickly compute string hash with cyrb53 algorithm
getVariableName(basicName, existingVariables?)basicName: string
existingVariables?: CollectionOf<string> | undefined
returns: string
input anything weird, get a valid variable name
optionally, you can give a existingVariables to avoid conflicting -- the new name might have a numeric suffix
getVariableName('foo-bar') // -> "fooBar"
getVariableName('123abc') // -> "_123abc"
getVariableName('') // -> "foobar"
getVariableName('name', ['name', 'age']) // -> "name2"
bracket(text1, text2, brackets?)text1?: string | number | null | undefined
text2?: string | number | null | undefined
brackets?: string | [string, string] | undefined โ defaults to [" (", ")"]
returns: string
Add bracket (parenthesis) to text
bracket("c_name", "Column Name") => "c_name (Column Name)"bracket("Column Name", "c_name") => "Column Name (c_name)"If one parameter is empty, it returns the other one:
bracket("c_name", null) => "c_name"bracket(null, "c_name") => "c_name"randomStr(size?, dict?)size?: number โ defaults to 16
dict?: string โ defaults to -\w same as nanoid
returns: string
Generate a random string
nanoid(size?, dict?)size?: number โ - defaults to 16
dict?: string โ - defaults to -\w same as nanoid
returns: string
alias of randomStr
value/compareshallowEqual(objA, objB, depth?)objA: any
objB: any
depth?: number โ defaults to 1
returns: boolean
value/jsontryParseJSON(text, fallback)text: string
fallback: T
returns: T
safe parse JSON string, return fallback if failed
value/typesisNil(obj)obj: any
returns: boolean
Tell if obj is null or undefined
isObject(obj)obj: any
returns: false | "array" | "object"
Tell if obj is Array, Object or other(false)
isThenable(sth)sth: any
returns: boolean
type MaybePromise<T>export type MaybePromise<T> = Promise<T> | T
type FalsyTypeScript type presents all falsy value, including Nil, false, 0, ""
export type Falsy = null | undefined | false | 0 | "";
type NilTypeScript type presents null or undefined
export type Nil = null | undefined;
type Fn<RET, ARGS>TypeScript type presents any function
export type Fn<RET = any, ARGS extends any[] = any[]> = (...args: ARGS) => RET;
FAQs
Some utils that I repeated too many times. DRY!
The npm package yon-utils receives a total of 48 weekly downloads. As such, yon-utils popularity was classified as not popular.
We found that yon-utils demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.ย It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.