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

@lp-labs/observable-map

v0.1.0

Published

A typed observable map with keyed subscriptions and batched changes.

Readme

@lp-labs/observable-map

A Map-like typed store that notifies subscribers of effective changes. It supports key-filtered listeners, nested batches, abortable subscriptions, and deterministic notification when listeners mutate the map reentrantly.

Import

import { ObservableMap } from "@lp-labs/observable-map";

const state = new ObservableMap<string, number>();

The package provides ESM and CommonJS entry points with matching TypeScript declarations. It has no runtime dependencies and requires Node.js 18 or newer.

API

Runtime exports:

  • ObservableMap<K, V> stores entries and exposes the familiar size, get, has, set, delete, clear, entries, keys, values, forEach, and iteration operations. The constructor optionally accepts iterable entries. set returns the map; delete returns whether a key existed. Setting an existing key to an Object.is-equal value and deleting/clearing absent data are no-ops.
  • ObservableMapDisposedError is thrown by mutators, subscribe, and batch after dispose().

Additional ObservableMap<K, V> members:

  • subscribe(listener, { key?, signal? }) returns an idempotent unsubscribe function. Without key, the listener receives all changes. With key, it receives only matching changes; batch and clear events are filtered to that key. An already-aborted signal creates no subscription.
  • batch(operation) executes synchronously and delivers the collected changes once as a batch event after the outermost batch completes. Nested batches contribute to the same event. Changes remain applied if the callback throws; queued changes are still notified before that exception is rethrown.
  • isDisposed indicates lifecycle state. dispose() is idempotent, removes listeners, clears pending notifications, and leaves read operations usable.

Events are discriminated by type:

  • set: { type, key, value, previous, hadPrevious }.
  • delete: { type, key, previous }.
  • clear: { type, entries } with the removed key/value pairs.
  • batch: { type, changes } containing the ordered individual changes.

Event objects and change/entry snapshot arrays are frozen before notification; objects stored as keys or values are not deep-frozen.

The runtime exports are ObservableMap and ObservableMapDisposedError. SetChange, DeleteChange, ClearChange, MapChange, BatchChange, ObservableMapEvent, MapListener, Unsubscribe, and SubscribeOptions are TypeScript-only exports.

Example

const state = new ObservableMap<string, number>();
const stop = state.subscribe((event) => console.log(event), { key: "count" });

state.batch((map) => {
  map.set("count", 1);
  map.set("other", 2);
  map.set("count", 3);
});
// The keyed listener receives one batch containing only the two "count" changes.

stop();
state.dispose();

Listeners run in subscription order from a snapshot. Mutations during dispatch are queued and notified after the current event has reached its listeners. Listener exceptions do not prevent other listeners or queued events from running; after the dispatch drains, the mutating call throws an AggregateError containing the listener errors. A reentrant mutation can therefore take effect even when the outer mutator subsequently throws that aggregate. If both a batch callback and a listener fail, batch throws an AggregateError containing the callback error and the listener aggregate.

Typing and limits

Keys and values are generic and map operations use JavaScript Map semantics. Subscriptions are synchronous and local to this instance. This class is not a proxy, persistence layer, cross-process store, or concurrency primitive; reentrant callbacks execute synchronously in deterministic queue order.

License

MIT.

Support

If this library is useful to you, consider supporting its maintenance with a donation via PayPal.

Author

Created by Libin Prasanth. Connect on LinkedIn for updates and discussion.