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

musicsocket

v0.1.2

Published

Universal client SDK for musicsocket — realtime now-playing for any framework, Node, Bun, or a plain <script> tag. SSE/WS/REST with auto-fallback, ready-made widgets, and OAuth2 helpers.

Readme

musicsocket

Universal client SDK for a musicsocket server. One install gives you realtime "now playing" in any frontend framework, Node, Bun, or a plain <script> tag — with automatic transport negotiation (SSE → WebSocket → REST polling), ready-made themeable widgets, and OAuth2 ("Log in with musicsocket") helpers.

npm install musicsocket

Why

  • Write the realtime engine once. All the transport, reconnect, fallback and parsing logic lives in the framework-agnostic core. Every framework adapter is a thin shim that maps one reactive store onto the platform's native reactivity.
  • Degrades gracefully. Asks for the best channel the runtime supports and silently falls back / reconnects. Worst case: 5s REST polling, everywhere.
  • Pick your import. musicsocket/react, /vue3, /svelte5, /solid, … or a global <script> build.

Quick start (core)

import { MusicSocket } from "musicsocket";

const ms = new MusicSocket("https://msk.armagan.rest");

const sub = ms.watch(["acc_1", "acc_2:privatekey"], { transport: "auto", live: true });
const unsubscribe = sub.subscribe((state) => {
  console.log(state.status, state.transport, state.entries);
});

// later
unsubscribe();
sub.close();

Framework adapters

| Import | Primitive | | --- | --- | | musicsocket/react · /preact · /reactnative | useNowPlaying(source, refs, opts) | | musicsocket/vue3 · /vue2 | useNowPlaying(...)ref | | musicsocket/svelte5 · /svelte4 | nowPlaying(...) store ($np) | | musicsocket/svelte5/runes | useNowPlaying(...) runes ($state, no $) | | musicsocket/solid | createNowPlaying(...) accessor | | musicsocket/lit | NowPlayingController | | musicsocket/angular | injectNowPlaying(...) signal · nowPlaying$(...) | | musicsocket/astro · /native · /widgets | custom elements + global build |

// React
import { useNowPlaying, useProgress } from "musicsocket/react";

function Card() {
  const { entries } = useNowPlaying("https://msk.armagan.rest", "acc_1", { live: true });
  const np = entries["acc_1"]?.nowPlaying;
  const { fraction } = useProgress(np ?? null);
  return np ? <p>{np.title} — {np.artist} ({Math.round(fraction * 100)}%)</p> : <p>Idle</p>;
}
<!-- Svelte (store API, works in 4 and 5) -->
<script>
  import { nowPlaying } from "musicsocket/svelte5";
  const np = nowPlaying("https://msk.armagan.rest", "acc_1", { live: true });
</script>
{#if $np.entries.acc_1?.nowPlaying}
  {$np.entries.acc_1.nowPlaying.title}
{/if}
<!-- Svelte 5 runes ($state — no $ prefix) -->
<script>
  import { useNowPlaying } from "musicsocket/svelte5/runes";
  const np = useNowPlaying("https://msk.armagan.rest", "acc_1", { live: true });
</script>
{#if np.entries.acc_1?.nowPlaying}
  {np.entries.acc_1.nowPlaying.title} ({np.status})
{/if}
// Angular — signals via DI (auto-cleans on destroy)
import { Component } from "@angular/core";
import { injectNowPlaying, MUSICSOCKET_BASE_URL } from "musicsocket/angular";

// app config: provide { provide: MUSICSOCKET_BASE_URL, useValue: "https://msk.armagan.rest" }

@Component({
  template: `{{ np().entries['acc_1']?.nowPlaying?.title ?? 'Idle' }}`,
})
export class NowPlayingCard {
  np = injectNowPlaying("acc_1", { live: true }); // Signal<PresenceState>
}
// Or as an Observable for the async pipe: nowPlaying$(baseUrl, "acc_1")

Watching multiple accounts

One subscription can watch many accounts at once; pull each out by id.

import { useNowPlaying, useNowPlayingEntries, pickEntry } from "musicsocket/react";

// whole state (entries is a map keyed by account id)
const { entries } = useNowPlaying("https://msk.armagan.rest", ["acc_1", "acc_2", "acc_3:key"]);

// or aligned to your refs
const [a, b, c] = useNowPlayingEntries("https://msk.armagan.rest", ["acc_1", "acc_2", "acc_3:key"]);

pickEntry / pickEntries work on any adapter's state (and on the core PresenceState), so Vue/Solid/Svelte/Lit/Angular get the same selectors.

One shared connection

Every watch() / hook against the same server shares a single connection, no matter how many components subscribe. Internally a connection hub keeps one transport subscribed to the union of all watched accounts and fans updates out to each watcher; it adds/removes accounts live (WS subscribe/unsubscribe ops, SSE/ poll re-query) and closes the connection automatically once nothing is watching. So 50 <music-socket-card>s on a page = 1 socket, not 50.

Custom labels / language

Every widget's texts are overridable — pick a built-in language (en, tr, or register your own with addLang) and/or override individual labels. This works on server-rendered widgets (forwarded as query params) and the client-rendered <music-socket-card>.

// URL builders / framework widgets
ms.svgUrl("acc_1", { lang: "tr", labels: { idle: "Müzik yok", nowPlaying: "Şu an" } });

import { resolveLabels, addLang } from "musicsocket";
addLang("de", { nowPlaying: "LÄUFT", paused: "PAUSIERT", idle: "Nichts läuft", loading: "Lädt…" });
const labels = resolveLabels("de", { idle: "Stille" });
<!-- custom texts on any element -->
<music-socket-card base-url="https://msk.armagan.rest" account="acc_1"
  lang="tr" label-idle="Şu an sessizlik"></music-socket-card>

<music-socket-svg base-url="https://msk.armagan.rest" account="acc_1"
  label-playing="ŞİMDİ" label-idle="Boşta"></music-socket-svg>

Plain <script> / no build step

<script src="https://unpkg.com/musicsocket/dist/native.global.js"></script>

<music-socket-card base-url="https://msk.armagan.rest" account="acc_1"></music-socket-card>

<script>
  const ms = new musicsocket.MusicSocket("https://msk.armagan.rest");
  ms.watch("acc_1").subscribe((s) => console.log(s.entries));
</script>

Widgets

URL builders for the server's visual endpoints, plus custom elements:

ms.svgUrl("acc_1", { theme: "midnight", layout: "compact" });   // streaming SVG
ms.pngUrl("acc_1", { theme: "spotify" });                        // static PNG
ms.iframeUrl("acc_1");                                            // hosted embed
<music-socket-svg base-url="https://msk.armagan.rest" account="acc_1" theme="nord"></music-socket-svg>

OAuth2 ("Log in with musicsocket")

const oauth = ms.oauth({ clientId: "app_123", redirectUri: location.origin + "/cb", scope: ["profile", "accounts.read"] });

// 1) kick off (PKCE handled for you)
await oauth.redirectToAuthorization();

// 2) on your redirect page
const tokens = await oauth.handleRedirect();
const info = await oauth.getUserInfo(tokens.access_token);

Transport negotiation

auto tries SSE, then WebSocket, then REST polling (pollIntervalMs, default 5000). Unsupported transports are skipped by capability detection; dropped connections retry with capped backoff and downgrade after repeated failure.

License

MIT