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

@console-one/collections

v0.2.1

Published

A home for collection data structures with different referencing and observation techniques. Ships LinkedQueue, ObservableQueue, IndexMap, LinkedHeap, SingleEntryObject, and HeapMap.

Readme

@console-one/collections

A home for collection data structures with different referencing and observation techniques. Package grows over time as new queue variants and collection types are needed.

Current contents

| Class | File | Purpose | |---|---|---| | LinkedQueue<T> | src/linked-queue.ts | Plain doubly-linked FIFO queue. No events, no insert-at-node. Use when you just want a fast queue. | | ObservableQueue<T> | src/observable-queue.ts | Doubly-linked FIFO queue with on(event, handler) push listeners and insert(data).before(node) / .after(node) for positional insertion. Use when you need to react to pushes or splice items by node reference. | | IndexMap<T> | src/index-map.ts | Map keyed by a monotonic counter — keys are unique and never reused. lock() reserves a key before the value is ready; set(key, value) fills it later. indexLock() returns an IndexLock<T> handle carrying a back-reference so a listener can un-register itself from inside its own closure. Useful for subscription IDs, allocation slots, and self-removing callbacks. | | LinkedHeap<T> | src/linked-heap.ts | Key-addressable binary heap whose items are exposed as HeapNode handles. Callers can update(data) in-place (preserving identity across rebalance), remove(key) by key, detach a node without reshaping, and async-iterate neighbors via nextNode() / prevNode(). Suitable for schedulers, priority queues whose priorities change, and reactive UIs. |

Future variants planned: different backing structures (array ring, chunked), different reference techniques (weak refs, handle-based), different observation models (per-item subscriptions, batched flush events).

Install

npm install @console-one/collections

Usage

LinkedQueue — the minimal case

import { LinkedQueue } from '@console-one/collections'

const q = LinkedQueue.of(1, 2, 3)
q.shift()   // 1
q.shift()   // 2
q.peak()    // 3 (peek without removing)
q.length    // 1

ObservableQueue — when you need push hooks or node-positional inserts

import { ObservableQueue } from '@console-one/collections'

const q = new ObservableQueue<string>()

// React to pushes
q.on('push', (unsub) => (newNodes, queue) => {
  console.log('pushed', newNodes.length, 'items; queue size:', queue.length)
})

q.push('alpha')
q.push('beta', 'gamma')

// Splice by node reference
q.push(1, 2, 4)  // [1, 2, 4]
const lastNode = q.last!
q.insert(3).before(lastNode)  // [1, 2, 3, 4]

IndexMap — stable handles before values exist

import { IndexMap } from '@console-one/collections'

const subs = new IndexMap<(msg: string) => void>()
const id = subs.store((msg) => console.log(msg))
// ... later
subs.delete(id)   // keys are never reused — safe to hand to external code

// Reserve a slot, fill it later:
const slot = subs.lock()
// ... wiring happens, the handler is built
subs.set(slot, (msg) => console.log('late', msg))

// IndexLock — self-unsubscribing listener
const lock = subs.indexLock()
lock.set((msg) => {
  if (msg === 'done') lock.remove()   // callback un-registers itself
  else handle(msg)
})

LinkedHeap — priority queue with stable handles

import { LinkedHeap, HeapNode } from '@console-one/collections'

const heap = new LinkedHeap<{ id: string; priority: number }>(
  (a, b) => a.data.priority - b.data.priority,
  (data) => data.id,   // mapper: derive the key from the value
)

const job = heap.push({ id: 'job-1', priority: 5 })
heap.push({ id: 'job-2', priority: 1 })

heap.peak()             // { id: 'job-2', priority: 1 }
job.update({ id: 'job-1', priority: 0 })  // in-place reprioritize
heap.peak()             // { id: 'job-1', priority: 0 }

// Async neighbor iteration — yields existing then future pushes
for await (const neighbor of heap.startNode!.nextNode()) {
  console.log('neighbor', neighbor.data)
}

Subpath imports

You can import each class directly if you want to avoid loading the others:

import { LinkedQueue } from '@console-one/collections/linked-queue'
import { ObservableQueue } from '@console-one/collections/observable-queue'
import { IndexMap, IndexLock } from '@console-one/collections/index-map'
import { LinkedHeap, HeapNode } from '@console-one/collections/linked-heap'

Shared API

Both queues implement the same base FIFO interface. They only differ in the extras that ObservableQueue adds.

Common methods

| Method | Description | |---|---| | push(...items) | Append one or more items to the tail. Returns a queue of any items that were flushed due to maxSize overflow. | | prepend(item) | Insert an item at the head. | | shift() / pull() | Remove and return the head item. Applies pipe option if configured. Throws if empty. | | remove() | Like shift() but without applying pipe. Throws if empty. | | peak() | Return the head item without removing it. Throws if empty. | | isEmpty() | True iff there are no items. | | length / size | Current number of items. | | concat(other) | Append all items from another queue. | | clear() | Remove everything. Chainable. | | toArray() | Materialize as a JS array, head first. | | toString() | Pretty-printed JSON of toArray(). | | headIs(pred) | Safe predicate check on the head; returns false if empty. | | flush(n) | Generator that pops up to n items. | | readHead(n?) | Generator over head-first items without consuming. | | readTail(n?) | Generator over tail-first items without consuming. | | [Symbol.iterator]() | Iterable, head first. | | static describes(x) | Type guard for instances of this queue class. | | static fromArray(arr) | Construct from an array. | | static of(...items) | Construct from varargs. |

Constructor options

Both queues accept the same options:

new LinkedQueue<T>(maxSize?: number, { pipe?, chain? })
  • maxSize — bound the queue. Items past the bound are flushed from the head. Default Number.POSITIVE_INFINITY.
  • pipe — transform applied on shift() / pull(). Leaves remove() untouched.
  • chain — factory for the "overflow" queue returned by push() when a bounded queue discards items.

ObservableQueue extras

| Method | Description | |---|---| | on(event, create) | Subscribe to a queue event ('push'). create(unsub) must return the actual handler; you get an unsubscribe function to close over. | | insert(data).before(node) / .after(node) | Splice a new item at a specific node position. Takes a QueueItem reference (from q.first, q.last, or walking .next / .prev). |

Known quirks

  • nodes() and some iterator loops use a !== vs || mix that can look infinite at a glance. They terminate because next becomes undefined. Don't use nodes() as a general-purpose iterator — use readHead() / [Symbol.iterator] instead.
  • maxSize overflow pushes into a secondary "chain" queue rather than silently dropping. The chain is created fresh on each push() call, so you don't accumulate one global overflow buffer.
  • pipe only runs on shift() / pull(), not on remove() or iterators.

Tests

npm test

46 tests covering FIFO semantics, insertion, options, overflow, event hooks, index-map lock/set/delete + IndexLock self-unsubscribe, and linked-heap push/pop/update/remove/async iteration.