@hyperfrontend/ list-utils§
Purpose-built collection utilities for queue management, filtering, and iteration patterns.
What is @hyperfrontend/list-utils?
@hyperfrontend/list-utils provides specialized collection utilities focused on common programming patterns that native JavaScript arrays don't handle elegantly. Rather than replicating lodash, this library targets specific use cases: FIFO/LIFO queue management with object reference tracking, cyclical value iteration, and string array sanitization.
The library enforces immutability through frozen interfaces while maintaining high performance. All queue operations (FIFO/LIFO) use native Set for O(1) lookups and guaranteed uniqueness, solving the common problem of accidentally adding duplicate items to task queues or event handlers.
Key Features
with type-safe object tracking
for cyclical iteration (ideal for round-robin patterns)
utilities (dedupe, trim, filter empty)
for loop-free number sequences
for common Map operations
Zero dependencies
Self-contained implementation with no third-party runtime dependencies
Why Use @hyperfrontend/list-utils?
Prevents Queue Bugs in Event-Driven Systems
Native arrays don't enforce uniqueness, making it easy to accidentally register the same event handler, task, or subscription multiple times. Both list kinds reject duplicates based on reference equality, eliminating a common source of memory leaks and duplicate processing in event loops, job queues, and observer patterns. A FIFO list throws on a duplicate push; a LIFO list ignores it.
Simplifies Round-Robin and Cyclical Patterns
The value picker solves the boilerplate problem of cycling through options (load balancer endpoints, color schemes, retry strategies). No manual index tracking or modulo math, just call next(). Particularly useful for testing scenarios where you need predictable value rotation.
Type-Safe String Sanitization
nonEmptyStrings() and uniqueStrings() handle the tedious work of cleaning user input, configuration arrays, or CSV parsing results. Filters null/undefined/empty/whitespace-only values in one call, with full TypeScript type narrowing.
Functional Composition Without Dependencies
All utilities return new arrays or frozen objects, never mutate inputs. This makes them safe for use in React hooks dependencies, Redux reducers, or any pure function context. No lodash required for these specific operations.
Installation
npm install @hyperfrontend/list-utils
Quick Start
import { createFifoList, createValuePicker, nonEmptyStrings, uniqueStrings } from '@hyperfrontend/list-utils'
// FIFO queue for task management
const taskQueue = createFifoList<{ id: string; execute: () => void }>()
taskQueue.push({ id: 'task1', execute: () => console.log('Task 1') })
taskQueue.push({ id: 'task2', execute: () => console.log('Task 2') })
const nextTask = taskQueue.pull() // Gets task1 (first in), or undefined once the queue is drained
// Round-robin value picker
const colorPicker = createValuePicker(['red', 'blue', 'green'])
colorPicker.next() // 'red'
colorPicker.next() // 'blue'
colorPicker.next() // 'green'
colorPicker.next() // 'red' (cycles back)
// String sanitization: blanks are dropped, survivors keep their original padding
const userInputs = [' hello ', '', 'world', null, 'hello', ' ', 'world']
const cleaned = uniqueStrings(nonEmptyStrings(userInputs)) // [' hello ', 'world', 'hello']
API Overview
Two ordered collections plus a few standalone helpers; the collections are the place to start. createFifoList and createLifoList each take no arguments, are generic over an object type, and return a frozen instance with the same fixed method surface: push, pull, map, forEach, remove, has, size and clear. Choosing one factory over the other decides which end pull() reads from and nothing else about the surrounding code. They diverge on one point, deliberately: pushing an item a FIFO list already holds throws, surfacing a queue that would otherwise take the same job twice, whereas a LIFO list ignores the duplicate and leaves the item where it already was.
Both are backed by a Set of the references you pushed, which has two consequences to know before reaching for either. A primitive is rejected at runtime, not only by the type parameter. And membership is reference identity, so two structurally identical objects are two entries, and has() and remove() want back the exact reference you handed over.
The helpers are independent of the lists and of each other. createValuePicker wraps a non-empty string array in a current() and next() pair that cycles forever, while createRange returns an inclusive run of numbers. For string arrays, nonEmptyStrings drops null, undefined, empty and whitespace-only entries but returns the survivors exactly as they arrived: it tests value.trim(), it does not trim, so padded values come through padded and uniqueStrings then dedupes on exact string equality, which leaves ' hello ' and 'hello' as two distinct results. The last helper, getLastKeyInMap, reads the most recently inserted key of a Map.
Every signature, type parameter and thrown error is in the API reference.
Compatibility
Runs on
- Node.js>=18.0.0supported
- Browserssupported
- Web Workerssupported
Ships as
- ESMTree-shakeable
- CJSNode and older bundlers
- IIFEHyperfrontendListUtils
- UMDHyperfrontendListUtils
CDN Usage
<!-- unpkg -->
<script src="https://unpkg.com/@hyperfrontend/list-utils"></script>
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@hyperfrontend/list-utils"></script>
<script>
const { createQueue, forEach } = HyperfrontendListUtils
</script>
Global variable: HyperfrontendListUtils
Architecture Highlights§
Both queue kinds hold object references only: pushing a primitive throws at runtime, not just at compile time. Membership is reference identity, so two structurally identical objects are two separate entries, and has() and remove() need the same reference you pushed.
API Reference§
ƒFunctions
Returns
FifoList< T>Example
Creating and using FIFO queue
interface Task { id: number; name: string }
const queue = createFifoList<Task>()
queue.push({ id: 1, name: 'first' })
queue.push({ id: 2, name: 'second' })
queue.pull() // => { id: 1, name: 'first' }
queue.pull() // => { id: 2, name: 'second' }
queue.pull() // => undefined (the list is empty)Returns
LifoList< T>Example
Creating and using LIFO stack
interface HistoryEntry { url: string; timestamp: number }
const history = createLifoList<HistoryEntry>()
history.push({ url: '/home', timestamp: 1 })
history.push({ url: '/about', timestamp: 2 })
history.pull() // => { url: '/about', timestamp: 2 }
history.pull() // => { url: '/home', timestamp: 1 }Parameters
Returns
number[ ]Example
Generating number ranges
createRange(1, 5)
// => [1, 2, 3, 4, 5]
createRange(3, 3)
// => [3]Parameters
| Name | Type | Description |
|---|---|---|
§values | string[ | The array of string values to cycle through |
Returns
ValuePickerExample
Cycling through values
const colorPicker = createValuePicker(['red', 'green', 'blue'])
colorPicker.current() // => 'red'
colorPicker.next() // => 'red' (first call initializes)
colorPicker.next() // => 'green'
colorPicker.next() // => 'blue'
colorPicker.next() // => 'red' (cycles back)Returns
KExample
Getting last key from Map
const userRoles = new Map<string, string>()
userRoles.set('alice', 'admin')
userRoles.set('bob', 'editor')
userRoles.set('charlie', 'viewer')
getLastKeyInMap(userRoles) // => 'charlie'Parameters
| Name | Type | Description |
|---|---|---|
§values | string[ | The array of strings to filter |
Returns
string[ ]Example
Filtering empty strings
nonEmptyStrings(['hello', '', ' ', 'world', null as unknown as string])
// => ['hello', 'world']Parameters
| Name | Type | Description |
|---|---|---|
§values | string[ | The array of strings to deduplicate |
Returns
string[ ]Example
Deduplicating strings
uniqueStrings(['apple', 'banana', 'apple', 'cherry', 'banana'])
// => ['apple', 'banana', 'cherry']◈Interfaces
Properties
Related reading§
- How-to
How to run async jobs one at a time, in order
My writes need to happen one at a time and in order, and the array-plus-busy-flag queue I wrote breaks the moment a second caller starts it, a rejected job leaves the flag stuck, or a retry queues the same work twice.
- Package
@hyperfrontend/network-protocol
Used by @hyperfrontend/network-protocol for message queue management
- Getting started
Getting Started
Set HyperFrontend up and embed a first feature.
- Architecture
Architecture Guide
How the packages fit together.

