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

@lyku/para-signals

v0.0.1-pre.4

Published

Reactive primitives — signal / derived / effect / batch / untrack / proxySignal. Pure JS, runs on any JS runtime. Foundation of the para:* library suite; other modules expose their state through this. Pre-release: API may change before 0.1.0.

Readme

@lyku/para-signals

Reactive primitives — signal, derived, effect, batch, untrack — plus resource-tied signals, async-source adapters, and rate-limit operators that the rest of the Para ecosystem relies on. Pure JS, no runtime deps, runs on any JS host (Node, Bun, Deno, browsers, Cloudflare Workers).

This is the runtime side of Para's reactive system. The .pts syntax sugar (signal x = 0, effect { … }, ~>, ->) compiles to imports from this package. Other modules in the para:* suite (para:audio, para:gpio, parabun:llm, etc.) expose their state through this package's primitives so reactive composition is uniform across the suite.

import { signal, derived, effect } from "@lyku/para-signals";

const count = signal(0);
const doubled = derived(() => count.get() * 2);

effect(() => console.log(count.get(), doubled.get())); // 0, 0

count.set(1); // logs: 1, 2
count.update(n => n + 1); // logs: 2, 4

Core primitives

| | | | --- | --- | | signal(initial) | Writable cell. Reads inside an effect register a dep; writes mark subscribers dirty and schedule a flush. | | derived(fn) | Lazy memoized computation. Re-runs when its deps change. Re-subscribes to its dynamic dep set on each evaluation. | | effect(fn) | Imperative subscriber. Returns a disposer. The fn may return a cleanup function that runs before each re-execution and on dispose. | | batch(fn) | Coalesce writes inside fn into a single flush. | | untrack(fn) | Read inside a tracked context without registering a dep. |

Effects are drained synchronously via a re-entrant guard, so signal.set() inside an effect appends to the queue rather than recursing.

Resource-tied signals — what only Para does

Hardware modules emit signals whose lifecycle is bound to a real underlying resource (mic, camera, file watcher, websocket). When the resource closes, those signals should become inert and observers should unwind cleanly. resource() is the primitive that makes that explicit:

import { resource } from "@lyku/para-signals";

const mic = resource(({ signal: sig, onDispose }) => {
  const peak = sig(0);
  const handle = openMic();           // pretend hardware
  handle.onPeak(v => peak.set(v));
  onDispose(() => handle.close());    // released on dispose, in reverse order
  return { peak };                    // becomes mic.peak
});

mic.peak.get();         // current peak level
mic.alive.get();        // boolean signal — true until dispose
mic.use(() => console.log(mic.peak.get())); // effect bound to resource lifetime
mic.dispose();          // close mic, run cleanups, alive flips to false,
                        // bound effects auto-tear-down

// Or with `using` syntax:
{
  using m = resource(...);
  // m disposed automatically at scope exit
}

The handle layers alive / dispose / [Symbol.dispose] / [Symbol.asyncDispose] / use(fn) on top of whatever the setup function returned. use(fn) is the key: bound effects auto-dispose when the resource closes, so consumers don't need defensive if (active.get()) guards everywhere.

Async-source adapters

Hardware emits streams (audio frames, sensor data, video frames). These adapters lift them into resource-tied signals — no manual pump loop in user code:

| | | | --- | --- | | fromAsyncIter(asyncIterable, initial?) | Pumps each yielded value into result.value. Disposing calls the iterator's return(). | | fromStream(readableStream, initial?) | Same for ReadableStream<T>. Cancels the reader on dispose. | | fromEventTarget(target, eventName, { initial?, map? }) | Listens for events; signal updates with map(event). Removes the listener on dispose. |

const live = fromStream(audioFrames, null);
effect(() => process(live.value.get()));
// later:
live.dispose();  // reader cancelled, stream gracefully released

Rate-limit operators

Hardware emits faster than UI consumers want. These operators wrap a source signal and emit at controlled cadence:

| | | | --- | --- | | throttled(source, ms) | Leading-edge: first change emits immediately; subsequent changes within ms coalesce into a trailing emit at window end. | | debounced(source, ms) | Emits only after ms of silence following the last change. |

Both return resources: result.value is the rate-limited signal, result.dispose() releases the underlying effect and clears any pending timer.

const peakSlow = throttled(mic.peak, 33);   // 30fps view of a 1000hz source
effect(() => render(peakSlow.value.get()));

Why not @preact/signals-core?

Two reasons. Supply-chain hygiene — keeping the leaf primitive self-contained means every Para package depends on code we read and own, not a transitive trust chain we don't control. Differentiation — the resource / stream-adapter / rate-limit surface above is the part Preact doesn't have, and it only composes cleanly because we own the core. Future direction: a store({...}) primitive for proxy-based fine-grained reactivity on plain objects, with TypedArray-backed derivations fused via @para/simd.

Status

private:true / 0.0.0-dev — pending the workspace split. See parabun.script.dev for the runtime-bundled story today.