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

@capillaryjs/capillary-devtools

v1.2.0

Published

Bounded causal recordings and composable Capillary flow inspection tools.

Readme

Capillary DevTools

Opt-in causal recordings and independently composable inspection views for Capillary applications. Follow a native interaction through values, derivations, query/command attempts, subscriptions, and the UI that consumes the result. Emitters without subscribers are also visible downstream leaves.

Compatibility and installation

Requires the diagnostics-enabled Capillary and Capillary UI 1.2 release lines. This checkout contains those framework additions; their release versions are prepared through the guarded release workflow, not by starting the demo. DevTools 1.0.0 is an unpublished initial release candidate in this checkout.

pnpm add @capillaryjs/capillary @capillaryjs/capillary-ui @capillaryjs/capillary-devtools

All packages are ESM. Capillary and Capillary UI are peers, never bundled copies. The headless ./model entry imports no UI code or browser globals.

Record and inspect

Start capture before constructing the application to discover the entire graph. Mount the inspector alongside, not inside, the application being inspected.

import {createCapillaryUiRuntime} from '@capillaryjs/capillary-ui'
import {TraceRecorder, TraceSelection, TracePlayback, TraceInspector,
    devtoolsDiagnosticScope} from '@capillaryjs/capillary-devtools'

const recorder = new TraceRecorder({verbose: true}).start({fromStart: true})
// Construct and mount your application here, using its normal runtime.
const selection = new TraceSelection()
const playback = new TracePlayback()
const tools = createCapillaryUiRuntime({diagnosticScope: devtoolsDiagnosticScope})
tools.registerStyles(TraceInspector)
tools.mount(tools.create(TraceInspector, {recording: recorder, selection, playback}), document.body)
tools.injectStyles()

Choose a recorded root, then a source or target if needed. The graph shows one node per identity, while the causal outline preserves every execution, including both paths through a diamond. Solid arrows are recorded causal hops. Dashed arrows are known connections/input provenance, not proof of execution. Expand Downstream leaves to select UI endpoints, callbacks, and unsubscribed emitters. Details show inputs, values, state, errors, attempt outcomes, and capture limitations. A node with no observed event is labelled as such.

Components show render calls separately from lifecycle-event counts. A render start and completion are two events, usually one render; synchronous reentrant passes are counted too. UI consumers are identified even when they have downstream children. DataTable has named header, column-header, and body consumers. A body render can change rows without rendering unchanged headers.

Consumer details show the trigger (dependency, parent, or explicit) and own renderer DOM writes. These counts exclude nested consumers and application DOM code; they include renderer writes to detached nodes. They are not paint/layout measurements. Zero own writes does not mean that child consumers did no work. Older recordings without this metadata show unknown effects. The composed inspector places node details beside the graph; standalone views remain independently composable.

First/previous/next, the range control, and optional timed playback inspect the recording only. They never rewind or rerun application work. The default is the complete flow, with no autoplay; timed playback starts only on explicit Play. Elapsed-time mode caps idle waits. Node geometry stays fixed while stepping.

For late attachment use recorder.start({roots: [searchEmitter, resultQuery]}). Known reachable connections can be discovered, but earlier activity, disposed objects, and disconnected objects cannot be reconstructed. fromStart: true is a caller assertion, not an automatic proof of capture completeness.

stop() pauses capture without releasing history; start() resumes with a gap notice; reset() clears history and marks an active restart as late capture. snapshot() returns an immutable flat recording. export() returns safe JSON. Destroy mounted views and dispose the recorder, selection, and playback when their owner ends. Disposing a recorder stops capture; its final snapshot remains available to the caller.

Capture and privacy

| Option | Default and meaning | | --- | --- | | maxEvents | 2,000 retained occurrences | | maxBytes | 2,000,000 estimated UTF-16 bytes of serialized records; not a heap-size guarantee | | maxNodes, maxEdges | 2,000 nodes and 8,000 connection lifetime records | | maxPreviewLength | 180 characters per preview | | maxSnapshotDepth, maxSnapshotEntries | 3 levels (maximum 20), 100 properties across each captured value tree | | capture | scalar: metadata and primitive previews; object contents are not read | | topology, ui, verbose | true, true, false; verbose adds unchanged recomputations and input snapshots | | clock | Date.now; use a monotonic/injected clock for measured intervals |

Payload modes are none, scalar, preview, snapshot, raw, and formatter. preview explicitly opts into shallow own-property inspection (at most eight retained fields, no getters or recursive copy). Proxy descriptor traps may run; failures are caught. raw deliberately retains mutable references, labels them non-historical, and excludes them from JSON before serialization, including toJSON. Their referenced heaps are outside the byte budget. formatter requires a pure (value, {nodeId, field}) => string function for application redaction. Formatter failures become a capture-error preview, not an application exception. Node labels/causes are metadata, not passed through payload redaction; do not put secrets there. Scalar strings can also be sensitive.

For trusted development data, opt into expandable capture-time object contents:

const recorder = new TraceRecorder({
    verbose: true,
    capture: 'snapshot',
    maxSnapshotDepth: 4,
    maxSnapshotEntries: 100,
    maxPreviewLength: 180,
}).start({fromStart: true})

snapshot copies nested own data properties into immutable ValueSnapshot trees. Details expand values, before-values, errors, and inputs without accessing the live objects. Arrays show their captured length. Cycles, accessors, descriptor failures, and depth/entry/string limits have explicit markers. Getters and toJSON are never called; proxy own-key/descriptor traps may run and failures are contained. Property enumeration itself follows JavaScript's Reflect.ownKeys behavior; the entry budget bounds retained/traversed properties, not a proxy's own execution time. Captured trees participate in maxBytes and JSON export. Original object references are not retained. This mode can retain sensitive fields; application policy must choose whether to use it.

Scalar recordings cannot recover omitted contents later: configure capture and record a new interaction. preview remains shallow; increasing its character limit does not make it recursive. raw is not a historical object snapshot.

Eviction is explicit; missing parents/metadata and unfinished attempts are never presented as a complete trace or proof that a request is still running. An unchanged node is only an observed unchanged recomputation in verbose mode. Otherwise no event means unknown, not “did not execute.”

Capture is opt-in and caller-owned. Importing the package does not start it. Deployment, authorization, data retention/export, and production inclusion are application policy. Use a formatter or none for sensitive payloads.

Compose only the views you need

import {TraceRecorder, TraceSelection, TracePlayback} from '@capillaryjs/capillary-devtools/model'
import {FlowGraphView} from '@capillaryjs/capillary-devtools/views/FlowGraphView'
import {TracePlaybackControls} from '@capillaryjs/capillary-devtools/views/TracePlaybackControls'

Every view accepts {recording, selection, playback?}, where recording is a recorder or an immutable snapshot. No view creates a recorder or application model. Independently exported views: CausalTraceView, ChronologicalTraceView, FlowGraphView, TraceTimelineView, ActivityOverviewView, TraceDetailsView, and TracePlaybackControls. TraceInspector is their optional workbench composition. Timeline is a precise attempt start/duration/outcome table; it does not infer background work.

Use runtime style registration for just the selected component dependencies, or import @capillaryjs/capillary-devtools/styles/structural.css for all views. CSS uses --cap-trace-text, -surface, -line, -accent, -selected, -muted, and -warning variables (all with neutral fallbacks). Flow graph nodes also use --cap-trace-kind-ui, -query, -command, -derived, -subscriber, -interaction, and -external for their default accents. Each flow node exposes its raw diagnostic kind as data-kind, allowing a host page to provide additional kind-specific styling. Shape and accent are supplementary cues: the native node label and kind text remain the authoritative accessible identification. Controls are native and keyboard-operable; the scrollable graph has an equivalent textual outline and leaf list. There is no implicit animation or reduced-motion exception.

The public model also exports filterTrace, traceRoots, causalOutline, projectFlow, traceAttempts, captureLimitations, TraceRecording, and their associated types. Schema/protocol version 1 separates immutable occurrences from stable identities and connection intervals. Graph layout handles feedback cycles without recursively executing the graph.

nodeValueEvent(nodeEvents) selects the last observed state rather than a later lifecycle marker's absent payload. Graph/node details use this rule, so a query's successful result is not replaced visually by an empty “operation closed” fact. While stepping, event details follow the cursor; choosing a node instead keeps its observed state in focus. Attempt tables describe the complete selected trace.

DevTools emitters and views use devtoolsDiagnosticScope, including internal controls. They cannot enter a recording through a captured parent. A tool that intentionally writes an existing application emitter does not hide that application write. Custom inspection components should use the same runtime scope; ordinary application components must not.

See the repository's diagnostics guide for explicit async causality and flow lab for the workbench and compact compositions.