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

lilies

v1.0.0

Published

A small, dependency-free doubly **li**nked **li**st in **E**CMA**S**cript. Published as **ESM-only** (`"type": "module"`).

Readme

🪷 lilies

A small, dependency-free doubly linked list in ECMAScript. Published as ESM-only ("type": "module").

Installation

npm install lilies
import LinkedList from "lilies";

Compatibility

⚠️ Remember to keep lilies away from cats 🐈 and cats away from lilies. These flowers are highly toxic to our fluffy friends!

Rationale

Arrays are the default sequential structure in JavaScript. They are great when you mostly access items by index and let the engine optimize dense storage. They are a worse fit when you need stable handles to elements and O(1) insertion or removal at a known node, because splice and shifting elements get expensive.

This library gives you a classic doubly linked list. Each push / unshift returns a LinkedListItem node you can keep. remove only updates pointers, so repeated inserts and removals in the middle stay predictable where arrays would have to move many elements.

The API is intentionally small: a few list operations, forward and backward traversal with optional early exit, and indexed access that picks the cheaper direction.

Common scenarios

  • Collections with frequent inserts or middle removals, such as objects in a 3D scene.
  • Removal by node without having to keep the index or do a lookup by value when removing.
  • Queues that are appended to from both ends or needing pending task cancellation with cheap cleanup.

If you mostly need random access by index or fast bulk iteration over compact storage, prefer Array (or TypedArray). If you mostly need O(1) structural change at a known link, use LinkedList.

API

new LinkedList<Item>()

Constructs an empty list of LinkedListType<Item>.

List structure

  • first / lastLinkedListItem<Item> | null; length — number of nodes.
  • LinkedListItem<Item>{ value, prev, next }.

Mutations

  • push(item) — append; returns the new LinkedListItem.
  • unshift(item) — prepend; returns the new LinkedListItem.
  • remove(item) — detach that node (must belong to this list).

Traversal

  • each(visitor, context?) — head to tail.
  • eachRight(visitor, context?) — tail to head.

visitor may be:

  1. A function invoked as visitor.call(context, element, index, loopControl). If you pass context, it becomes this inside the function.
  2. A visitor object with call(context, element, index, loopControl) — see Visitors below.

loopControl is a LoopControlType for the current pass:

  • stop(result?) — end traversal early; optional result is returned from each / eachRight.
  • broken / result — reflect whether the loop stopped and any value passed to stop.

The traversal methods return the optional result from stop, or undefined if the walk reaches the end.

Indexed access

  • at(index) — returns the value at index. It walks from the nearer end.
    Throws RangeError if index is out of range (including an empty list).

Types for LinkedListItem, LinkedListType, and LoopControlType are shipped in the generated .d.ts files alongside the implementation.

Performance insights

Indexed access: Arrays are usually orders of magnitude faster. If your code mostly does "get me item at i", use an array.

Scanning the whole collection: Arrays are usually a bit faster, but not by a dramatic amount. Do not choose this library for scan speed alone.

Removing by handle: LinkedList is usually much faster when you already have the node you want to remove. Frequent removals at known positions are the main reason to use this library.

Appending: Usually close enough that it should not drive the decision. Choose based on access and removal patterns instead.

Visitor vs function: A visitor object can be around 2-3x faster than a closure in tight traversal code. Only care about this in hot paths or very large lists.

Visitors and why they matter here

A visitor (in this package) is an object that implements call(context, element, index, loopControl). It plays the same role as a callback function, but it is also a natural place to keep traversal state such as sums, counters, flags, or collected values.

Why use it:

  • Natural place for state — Counters, accumulators, and configuration live on the visitor itself. That makes it a good fit for reductions and other stateful passes.
  • Often faster than a closure — In hot traversal code, using a visitor can be around 2-3x faster than using a closure.
  • Can be reused — If you want, the same visitor instance can be used across multiple traversals, which can be beneficial for extremely performance-sensitive code like a 3D game engine.

Of course plain function callbacks remain fully supported and are usually clearer for one-off traversals. When you are optimizing a very hot loop, prefer a visitor.

License

MIT