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

stagelinq-js

v0.3.1

Published

A zero-dependency, symmetric-peer implementation of the Denon StageLinq protocol for Node.js

Readme

stagelinq.js

CI codecov npm version license

A zero-dependency Node.js / TypeScript implementation of Denon/inMusic's StageLinq protocol, used by Prime-series DJ hardware (SC5000/SC6000, Prime 2/4/Go, X1800/X1850) to share live performance data with lighting, streaming, and production software.

This is a from-scratch implementation, not a fork or translation of any existing library. Design is informed by the best current understanding of the StageLinq protocol. Regardless, this project leans heavily on the hard work of many in the community:

  • icedream (@icedream) - The OG! Did the first, ground-breaking reverse-engineering work on Stagelinq. Maintainer of the go-stagelinq library
  • MarByteBeep (@MarByteBeep) - Author of the original TypeScript implementation.
  • Chris Le / Triode (@chrisle) - Maintainer of the StageLinq TypeScript library and creator of the Now Playing app
  • Matt Hite (@mhite) - go-stagelinq contributor and collaborator in the reverse-engineering of many services
  • Domas Zelionis (@dzelionis) - Collaborator in the reverse-engineering of the BeatInfo service, and author of it's Python implementation
  • DJ LiQuiD iCe (@djliquidice) - Active Denon DJ forum member, Radio host, whose work and knowledge of the database format have been an invaluable resource

Design Goals

  • Protocol correctness. Strive to replicate the behaviour of devices and software which use the official StageLinq SDK - On-spec, if we had access to the official spec, that is... 😉
  • Lean core. The core sticks to discovery, the wire format, and connection negotiation — it doesn't track application state (what's playing, what's synced) or touch the filesystem. Loading and persisting a device identity is left to the application (see examples/config.ts); none of that belongs down here.
  • Self-contained services. Each service (StateMap, BeatInfo, TimeSync, Syncing, Broadcast, FileTransfer) lives in its own folder under src/services/ and only depends on src/core, never on another service. This is enforced by lint rule, not just convention (see eslint.config.js).
  • Symmetric peers. A StageLinq network-participant isn't a client that dials into services, or a server that waits for clients - it's a peer that can initiate or accept connections; behaviour which is dictated by the numerical relationship between their deviceIds.

Novel Functionality - Limitless Usability

What cool, new functionality can you accomplish with this library? Here's a short list of what we've managed to demonstrate so far:

  • Browse remote filesystems. (examples/filetransfer-browse.ts) - Inspired by @mhite's demonstration, you can traverse a remote source's files and directories via the command line.
  • Keep downloaded files synced with remote. (examples/filetransfer-watch.ts) - When a remote player makes changes to a database you've downloaded, it sends a 0x6 DataUpdate message indicating which chunks of the file have changed. This example downloads these chunks, and patches them into the local copy of the file.
  • Serve your own StateMap states. (examples/statemap.ts) - If a peer asks you for a state, you can respond() with real data or reject() it. The example demonstrates accepting connections and answering requests, but rejects every one since it has no real deck state to draw from — wiring in actual state is left to your application.

Have something else in mind? You aren't limited by what we've thought of. The application has freedom to handle responses and craft it's own replies.

Status: pre-release

The protocol layer is fairly solid — confirmed against real SC6000 players and their OfflineAnalyzer companions. The application-facing API is still finding its shape and may change before 1.0.

Discovery, Directory, and every listed service are implemented and have been confirmed working against real hardware (Denon SC6000 players and their OfflineAnalyzer companion processes) — see examples/ for the live-testing scripts this was verified with, and docs/PROTOCOL.md / docs/FILETRANSFER_FINDINGS.md for exactly what's confirmed on the wire versus still an open question. Some corners of the protocol are deliberately left unconfirmed rather than guessed at — e.g. FileTransfer's push/write-back flow has no confirmed "the write succeeded" signal yet — those gaps are called out explicitly in the docs.

Project layout

src/
  core/            shared wire codec, discovery, connection/role negotiation
  services/
    directory/     per-peer service registry + discovery handshake — every
                    other service's availability is announced through this
    statemap/      deck/mixer parameter subscribe + serve
    beatinfo/      real-time beat/bpm stream
    timesync/      clock/epoch ping-pong between peers
    syncing/       master-deck beat/tempo broadcast + handoff
    broadcast/     arbitrary key/value database-change push, no request/reply
    filetransfer/  Engine Library database + track file browsing, reads,
                    and live update watching
  peer.ts          optional orchestration layer over Discovery + Directory +
                    however many service servers an application registers
  index.ts         public entry point
examples/          manual, live-testing scripts run against real hardware
docs/
  PROTOCOL.md               confirmed, cross-service protocol facts
  FILETRANSFER_FINDINGS.md  detailed FileTransfer evidence trail, capture by capture
  DEVICEID_FINDINGS.md      DeviceId structure/generation analysis, capture by capture
  KNOWN_STATES.md           StateMap paths seen on real hardware, copy-pasteable
                            into example:statemap
test/
  core/
  services/
  examples/
  peer.test.ts

Examples

Every service has a manual test script under examples/, meant to run against real hardware on the same network — these aren't automated tests (see test/ for those). Each one loads a stable device identity via .env on first run, using examples/config.ts — see that file's doc comment for why that's example-level policy rather than something Peer decides for you.

pnpm example:discover                # Discovery only — see what's on the network
pnpm example:directory               # Discovery + Directory service negotiation
pnpm example:statemap                # Subscribe to a handful of deck/mixer states
pnpm example:beatinfo                # Stream a peer's real-time beat clock
pnpm example:timesync                # Exchange clock/epoch pings
pnpm example:syncing                 # Watch master-deck beat/tempo + handoff traffic
pnpm example:broadcast               # Watch arbitrary database-change broadcasts
pnpm example:filetransfer-browse     # Interactively browse a peer's Engine Library
pnpm example:filetransfer-download   # Download a specific file by path
pnpm example:filetransfer-watch      # Download a file once, then mirror live updates to it

Some real hardware (e.g. a companion OfflineAnalyzer process) is configured with a DeviceId that makes this software always the side being dialed, never the one dialing out — the three filetransfer-* scripts handle this with an interactive picker over every peer seen either way; see docs/PROTOCOL.md for the underlying DeviceId dial convention.

Protocol notes

Wire-format facts shared across more than one service — the DeviceId dial convention, the self-announcement frame shape every service's connect handshake reuses, TimeAlive semantics — live in docs/PROTOCOL.md rather than getting repeated in each service's code. FileTransfer also has docs/FILETRANSFER_FINDINGS.md, the detailed, capture-by-capture evidence trail behind everything PROTOCOL.md calls confirmed for that service. DeviceId structure and generation has its own equivalent write-up in docs/DEVICEID_FINDINGS.md. StateMap paths observed on real hardware are collected in docs/KNOWN_STATES.md — a good starting point if you want to subscribe to something examples/statemap.ts doesn't already.

Development

pnpm install
pnpm build       # compiles src/ to dist/
pnpm typecheck   # type-checks src/ and examples/, no emit
pnpm test        # runs the automated test suite (vitest)
pnpm lint        # eslint, including the core/service dependency boundary rule