
Product
Introducing Socket Firewall Enterprise: Flexible, Configurable Protection for Modern Package Ecosystems
Socket Firewall Enterprise is now available with flexible deployment, configurable policies, and expanded language support.
@cyca/utils
Advanced tools
A collection of utility functions and hooks for my TypeScript/JavaScript projects.
npm install @cyca/utils
# or
pnpm add @cyca/utils
# or
bun add @cyca/utils
invariant(condition, message)Throws an error if the condition is falsey. Inspired by tiny-invariant.
import { invariant } from '@cyca/utils'
invariant(typeof value === 'string', 'value must be a string')
Parameters:
condition: any - The condition to checkmessage: string | (() => string) - The message to throw (or a callback to generate the message)Throws: InvariantError if condition is falsey
invariantResponse(condition, message, responseInit?)Throws a 400 Response if the condition is falsey. Useful for server-side validation.
import { invariantResponse } from '@cyca/utils'
invariantResponse(typeof value === 'string', 'value must be a string')
Parameters:
condition: any - The condition to checkmessage: string | (() => string) - The message to throwresponseInit?: ResponseInit - Additional response init optionsThrows: Response if condition is falsey
isValidHttpUrl(string)Checks if a string is a valid HTTP URL.
import { isValidHttpUrl } from '@cyca/utils'
if (isValidHttpUrl('https://example.com')) {
// valid URL
}
delay(ms, value?)Promise wrapper around setTimeout.
import { delay } from '@cyca/utils'
await delay(1000) // wait 1 second
const result = await delay(500, 'value') // wait 500ms and return 'value'
Parameters:
ms: number - Milliseconds to waitvalue?: T - Optional value to resolve withReturns: Promise<unknown>
prependHttp(url, options?)Prepends http:// or https:// to a URL if it doesn't already have a protocol.
import { prependHttp } from '@cyca/utils'
prependHttp('example.com') // 'http://example.com'
prependHttp('example.com', { https: true }) // 'https://example.com'
Parameters:
url: string - The URL to prependoptions?: { https?: boolean } - Options objectReturns: string
getUrlImageSize(url)Gets the width and height of an image from a URL.
import { getUrlImageSize } from '@cyca/utils'
const { width, height } = await getUrlImageSize('https://example.com/image.jpg')
Returns: Promise<{ width: number; height: number }>
toBase64(str)Converts a string to base64.
import { toBase64 } from '@cyca/utils'
const encoded = toBase64('hello world')
escapeRegExp(string)Escapes special characters in a string for use in a regular expression.
import { escapeRegExp } from '@cyca/utils'
const escaped = escapeRegExp('hello.world')
higlightWords(textToHighlight, searchTerm, options?)Highlights words in text that match a search term.
import { higlightWords } from '@cyca/utils'
const result = higlightWords('Hello world', 'world', {
caseSensitive: false,
mark: (part, matched) => matched ? `<mark>${part}</mark>` : part
})
Parameters:
textToHighlight: string - The text to search insearchTerm: string - The term to search foroptions? - Configuration object
caseSensitive?: boolean - Whether to match casemark?: (part: string, matched: boolean) => T - Function to transform matched/unmatched partsappend?: (accum: T, part: P) => T | null - Function to accumulate resultsdebounce(func, timeout)Creates a debounced function that delays invoking func until after timeout milliseconds.
import { debounce } from '@cyca/utils'
const debouncedFn = debounce(() => console.log('called'), 300)
debounceAnimationFrame(func)Creates a debounced function that uses requestAnimationFrame.
import { debounceAnimationFrame } from '@cyca/utils'
const debouncedFn = debounceAnimationFrame(() => console.log('called'))
noop()A no-op function that returns null.
import { noop } from '@cyca/utils'
const callback = noop()
deepOmit(object, path)Deeply omits a property from an object using a path array.
import { deepOmit } from '@cyca/utils/deep-omit'
const obj = { a: { b: { c: 1, d: 2 } } }
const result = deepOmit(obj, ['a', 'b', 'c'])
// result: { a: { b: { d: 2 } } }
Can also be used in curried form:
const omitPath = deepOmit(['a', 'b', 'c'])
const result = omitPath(obj)
Type-safe: The path is type-checked against the object structure.
useAtomCallback(callback, options?)A custom version of Jotai's useAtomCallback that keeps the latest callback identity.
import { useAtomCallback } from '@cyca/utils/jotai'
const handleClick = useAtomCallback((get, set) => {
const value = get(someAtom)
set(anotherAtom, value + 1)
})
Parameters:
callback: (get: Getter, set: Setter, ...args: Args) => Result - The callback functionoptions?: Options - Jotai store optionsReturns: A memoized callback function
useSelectAtom(atom, selector)Selects a derived value from an atom using a selector function.
import { useSelectAtom } from '@cyca/utils/jotai'
const userName = useSelectAtom(userAtom, (user) => user.name)
Parameters:
atom: Atom<V> - The atom to read fromselector: (v: V) => S - Function to derive valueReturns: The selected value
withSnapshotHistory(targetAtom, limit?)Wraps an atom with undo/redo history functionality.
import { withSnapshotHistory, COMMIT, UNDO, REDO, RESET } from '@cyca/utils/jotai'
const countAtom = atom(0)
const historyAtom = withSnapshotHistory(countAtom, 10)
// In a component:
const [{ value, previous, canUndo, canRedo }, dispatch] = useAtom(historyAtom)
// Update the value (doesn't create history entry)
dispatch(5)
// Commit to history
dispatch(COMMIT)
// Undo/redo
dispatch(UNDO)
dispatch(REDO)
// Reset history
dispatch(RESET)
Parameters:
targetAtom: AnyWritableAtom<T> - The atom to tracklimit?: number - Maximum history entries (default: unlimited)Actions:
COMMIT - Record current value to historyUNDO - Move back one stepREDO - Move forward one stepRESET - Clear all historySetStateAction<T> - Update value without creating historyinit(callback, store?)Initializes state or side effects when the store is created.
import { init } from '@cyca/utils/jotai'
const cleanup = init((get, set, store) => {
// Initialize something
set(someAtom, initialValue)
// Return cleanup function
return () => {
// Cleanup
}
})
Parameters:
callback: (get: Getter, set: Setter, store: Store) => void | (() => void) - Initialization functionstore?: Store - Jotai store (uses default if not provided)Returns: Cleanup function if provided by callback
mitt(all?)Creates a tiny event emitter.
import { mitt } from '@cyca/utils/mitt'
type Events = {
'user:login': { id: string }
'user:logout': void
}
const emitter = mitt<Events>()
emitter.on('user:login', (data) => {
console.log('User logged in:', data.id)
})
emitter.emit('user:login', { id: '123' })
Parameters:
all?: EventHandlerMap<Events> - Optional initial handlersReturns: Emitter<Events> with methods:
on(type, handler) - Subscribe to eventoff(type, handler) - Unsubscribe from eventemit(type, event) - Emit eventall - Map of all handlersbind(emitter, event, listener)Binds a listener to an event and returns an unbind function.
import { mitt, bind } from '@cyca/utils/mitt'
const emitter = mitt()
const unbind = bind(emitter, 'event', (data) => {
console.log(data)
})
// Later...
unbind()
Returns: () => void - Unbind function
bindAll(emitter, listeners)Binds multiple listeners at once and returns an unbind function.
import { mitt, bindAll } from '@cyca/utils/mitt'
type Events = {
'login': { id: string }
'logout': void
}
const emitter = mitt<Events>()
const unbind = bindAll(emitter, {
login: (data) => console.log('Login:', data.id),
logout: () => console.log('Logout')
})
// Later...
unbind()
Returns: () => void - Unbind function that removes all listeners
useMediaQuery(query)Hook to match media queries.
import { useMediaQuery } from '@cyca/utils/react'
const isMobile = useMediaQuery('(max-width: 768px)')
Returns: boolean | undefined - undefined during SSR
useClientLayoutEffectUse useEffect during SSR and useLayoutEffect in the browser to avoid warnings.
import { useClientLayoutEffect } from '@cyca/utils/react'
useClientLayoutEffect(() => {
// Safe to use in SSR
}, [])
useConstant(input)Creates a value exactly once. Unlike useMemo, this is guaranteed.
import { useConstant } from '@cyca/utils/react'
const expensiveObject = useConstant(() => createExpensiveObject())
usePreviousEffect(effect, inputs)Runs an effect with access to previous dependency values.
import { usePreviousEffect } from '@cyca/utils/react'
usePreviousEffect(([prevCount]) => {
console.log(`Count changed from ${prevCount} to ${count}`)
}, [count])
useHasChangedEffect(effect, deps, equal?)Runs an effect only when a dependency has changed.
import { useHasChangedEffect } from '@cyca/utils/react'
useHasChangedEffect(() => {
console.log('User changed')
}, [user], (a, b) => a.id === b.id)
usePreviousValue(value)Returns the previous value from the last render.
import { usePreviousValue } from '@cyca/utils/react'
const previousCount = usePreviousValue(count)
useHasChanged(value)Returns true if the value has changed since the last render.
import { useHasChanged } from '@cyca/utils/react'
const hasChanged = useHasChanged(count)
useEvent(callback)Creates an always-stable function identity. Easier to use than useCallback.
import { useEvent } from '@cyca/utils/react'
const handleClick = useEvent(() => {
console.log(count) // Always has latest value
})
useLatest(value)A React helper hook for storing latest value in ref object (updated in useLayoutEffect's callback). Similar to useEvent()
import { useLatest } from '@cyca/utils/react'
const valRef = useLatest(value)
// latest val
valRef.current
useAsyncCallback()Returns a handler with pending states for async operations.
import { useAsyncCallback } from '@cyca/utils/react'
const [{ pending, success, error }, wrapAsync] = useAsyncCallback()
const handleSubmit = wrapAsync(async () => {
await api.submit()
})
if (pending) return <Spinner />
if (error) return <Error error={error} />
Returns: [{ pending: boolean, success: boolean, error: Error | null }, wrap]
useEffectOnce(effectCb)Runs an effect only once, even in strict mode.
import { useEffectOnce } from '@cyca/utils/react'
useEffectOnce(() => {
console.log('Runs only once')
})
useDeepMemo(memoFn, key)Memoizes a value using deep equality comparison.
import { useDeepMemo } from '@cyca/utils/react'
const memoizedValue = useDeepMemo(() => {
return computeExpensiveValue(obj)
}, [obj])
useDeepMemoEffect(callback, deps)Like useEffect but with deep equality comparison for dependencies.
import { useDeepMemoEffect } from '@cyca/utils/react'
useDeepMemoEffect(() => {
console.log('Object deeply changed')
}, [complexObject])
useForceUpdate()Returns a function to force a re-render.
import { useForceUpdate } from '@cyca/utils/react'
const [key, forceUpdate] = useForceUpdate()
// Later...
forceUpdate() // Forces re-render
useIntersectionCallback(callback, options?)Detects element visibility using Intersection Observer.
import { useIntersectionCallback } from '@cyca/utils/react'
const ref = useIntersectionCallback((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('Element is visible')
}
})
}, { threshold: 0.5 })
return <div ref={ref}>Content</div>
useIsIntersecting(options?)Returns a boolean indicating if element is intersecting and a ref callback.
import { useIsIntersecting } from '@cyca/utils/react'
const [isVisible, ref] = useIsIntersecting({ threshold: 0.5 })
return <div ref={ref}>{isVisible ? 'Visible' : 'Hidden'}</div>
useIsVisible, useInViewAliases for useIsIntersecting.
useFocusElementOnVisible()Automatically focuses an element when it becomes visible.
import { useFocusElementOnVisible } from '@cyca/utils/react'
const ref = useFocusElementOnVisible()
return <input ref={ref} />
useCounter(initialValue?)A simple counter abstraction.
import { useCounter } from '@cyca/utils/react'
const { count, increment, decrement, reset, setCount } = useCounter(0)
useSuspendAfterMount(callback)Defers a suspense-triggering function until after mount.
import { useSuspendAfterMount } from '@cyca/utils/react'
const data = useSuspendAfterMount(() => fetchData())
useLazyRef(fn)A wrapper for useRef with lazy initialization.
import { useLazyRef } from '@cyca/utils/react'
const ref = useLazyRef(() => new ExpensiveClass())
useInterval(callback, delay)Runs a callback at a regular interval.
import { useInterval } from '@cyca/utils/react'
useInterval(() => {
console.log('Tick')
}, 1000)
useAnimationFrame(cb, deps)Runs a callback on every animation frame.
import { useAnimationFrame } from '@cyca/utils/react'
useAnimationFrame(({ time, delta }) => {
// Animation logic
}, [])
useStyleMeasure(prefix?)Dynamically measures element size and updates CSS custom properties.
import { useStyleMeasure } from '@cyca/utils/react'
const ref = useStyleMeasure('box')
// Sets --box-width and --box-height CSS variables
return <div ref={ref}>Content</div>
withHook(useHook)Converts a React hook into a render prop component.
import { withHook } from '@cyca/utils/react'
const WithCounter = withHook(useCounter)
<WithCounter args={[0]}>
{({ count, increment }) => (
<button onClick={increment}>{count}</button>
)}
</WithCounter>
withSuspense(Component, Fallback?)Wraps a component with Suspense boundary.
import { withSuspense } from '@cyca/utils/react'
const UserProfile = withSuspense(
UserProfileComponent,
() => <Spinner />
)
MIT
FAQs
Personal JS utils I use in the daily
We found that @cyca/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.

Product
Socket Firewall Enterprise is now available with flexible deployment, configurable policies, and expanded language support.

Security News
Open source dashboard CNAPulse tracks CVE Numbering Authorities’ publishing activity, highlighting trends and transparency across the CVE ecosystem.

Product
Detect malware, unsafe data flows, and license issues in GitHub Actions with Socket’s new workflow scanning support.