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

@adamthedeveloper/pretzel-sdk

v0.1.10

Published

This document covers all currently published consumer features for: - `@adamthedeveloper/pretzel-sdk` (`0.1.2`) - `@adamthedeveloper/pretzel-sdk-react` (`0.1.2`)

Downloads

50

Readme

Pretzel SDK Consumer Guide

This document covers all currently published consumer features for:

  • @adamthedeveloper/pretzel-sdk (0.1.2)
  • @adamthedeveloper/pretzel-sdk-react (0.1.2)

Install

npm install @adamthedeveloper/pretzel-sdk @adamthedeveloper/pretzel-sdk-react

@adamthedeveloper/pretzel-sdk-react requires peer dependencies:

  • react ^19.0.0
  • react-dom ^19.0.0

Package Features

@adamthedeveloper/pretzel-sdk

  • WebSocket connect/disconnect lifecycle
  • Automatic heartbeat (ping) every 15 seconds
  • Automatic reconnection with exponential backoff
    • initial delay: 500ms
    • max delay: 10,000ms
  • Per-user event subscriptions with explicit unsubscribe
  • Presence read APIs:
    • getPresence(userId)
    • getPresenceBatch(userIds)
  • Typing APIs:
    • startTyping(userId)
    • stopTyping(userId)
  • Event dispatch support for:
    • user_online
    • user_offline
    • typing_start
    • typing_stop
  • Tenant-safe request headers derived from apiKey

@adamthedeveloper/pretzel-sdk-react

  • PresenceProvider for sharing one PresenceClient instance
  • usePresenceClient() for direct client access in components
  • usePresence(userId) for single-user reactive presence state
  • usePresenceBatch(userIds) for multi-user reactive presence map
  • useTyping(userId) for reactive typing status
  • Built-in stale-event protection using event timestamps for:
    • usePresence
    • useTyping
  • Automatic subscribe/unsubscribe lifecycle inside hooks

Core Types

type PresenceStatus = "online" | "offline";

type UserPresence = {
  userId: string;
  status: PresenceStatus;
  last_seen: string | null;
};

type PresenceEvent = {
  type: "user_online" | "user_offline" | "typing_start" | "typing_stop";
  userId: string;
  tenantId: string;
  timestamp: string;
};

SDK API Reference

Create client

import { PresenceClient } from "@adamthedeveloper/pretzel-sdk";

const client = new PresenceClient();

connect(options)

client.connect({
  url: "ws://localhost:3001",
  httpUrl: "http://localhost:3000",
  apiKey: "<keyId>.<secret>",
  userId: "user-123",
});
  • url: WebSocket base URL
  • httpUrl (optional): HTTP base URL for presence reads; if omitted, derived from url
  • apiKey: issued presented API key in <keyId>.<secret> format
  • userId: current connected user

disconnect()

Stops heartbeat and reconnection attempts, removes listeners, and disconnects the socket.

subscribeToUser(userId, callback)

const unsubscribe = client.subscribeToUser("user-456", (event) => {
  console.log(event.type, event.timestamp);
});
  • Each call creates an independent subscription
  • Always call unsubscribe() during cleanup to avoid leaks

getPresence(userId)

const presence = await client.getPresence("user-456");

Returns:

{
  userId: "user-456",
  status: "online" | "offline",
  last_seen: string | null
}

getPresenceBatch(userIds)

const presenceList = await client.getPresenceBatch(["user-1", "user-2"]);

Returns UserPresence[].

startTyping(userId) / stopTyping(userId)

client.startTyping("user-456");
client.stopTyping("user-456");

React API Reference

PresenceProvider

import { PresenceClient } from "@adamthedeveloper/pretzel-sdk";
import { PresenceProvider } from "@adamthedeveloper/pretzel-sdk-react";

const client = new PresenceClient();

client.connect({
  url: "ws://localhost:3001",
  httpUrl: "http://localhost:3000",
  apiKey: "<keyId>.<secret>",
  userId: "user-123",
});

export function AppRoot() {
  return <PresenceProvider client={client}>{/* app */}</PresenceProvider>;
}

usePresenceClient()

Returns the PresenceClient from context.
Throws if used outside PresenceProvider.

usePresence(userId)

const presence = usePresence("user-456");
// { status: "online" | "offline", last_seen: string | null }

Behavior:

  • Fetches initial state via getPresence
  • Subscribes to real-time presence events
  • Ignores out-of-order stale events using timestamps

usePresenceBatch(userIds)

const presenceByUserId = usePresenceBatch(["user-1", "user-2"]);
// Record<string, { status: "online" | "offline"; last_seen: string | null }>

Behavior:

  • Fetches initial batch via getPresenceBatch
  • Creates one live subscription per user ID
  • Returns {} for an empty input list

useTyping(userId)

const { isTyping } = useTyping("user-456");

Behavior:

  • Listens to typing_start / typing_stop
  • Ignores stale events using timestamps
  • Resets to isTyping: false on cleanup

End-to-End Usage Example

import { useEffect } from "react";
import { PresenceClient } from "@adamthedeveloper/pretzel-sdk";
import {
  PresenceProvider,
  usePresence,
  useTyping,
  usePresenceClient,
} from "@adamthedeveloper/pretzel-sdk-react";

const client = new PresenceClient();

client.connect({
  url: "ws://localhost:3001",
  httpUrl: "http://localhost:3000",
  apiKey: "<keyId>.<secret>",
  userId: "viewer-user",
});

function UserPresenceCard({ userId }: { userId: string }) {
  const presence = usePresence(userId);
  const { isTyping } = useTyping(userId);
  const sdkClient = usePresenceClient();

  useEffect(() => {
    sdkClient.startTyping(userId);
    return () => {
      sdkClient.stopTyping(userId);
    };
  }, [sdkClient, userId]);

  return (
    <div>
      <div>{userId}</div>
      <div>Status: {presence.status}</div>
      <div>Last seen: {presence.last_seen ?? "never"}</div>
      <div>Typing: {isTyping ? "yes" : "no"}</div>
    </div>
  );
}

export function App() {
  return (
    <PresenceProvider client={client}>
      <UserPresenceCard userId="target-user" />
    </PresenceProvider>
  );
}

Error and Input Notes

  • connect() must be called before HTTP presence reads
  • apiKey must be <keyId>.<secret>
  • userId must be a non-empty string
  • getPresenceBatch(userIds) requires at least one user ID
  • Non-2xx presence HTTP responses throw an error
  • usePresenceClient() throws if no PresenceProvider is present

Service Expectations

  • WS service should be reachable at your configured url
  • Query API uses httpUrl when provided
  • If httpUrl is omitted, Query API is inferred from WS URL host:
    • ws://host -> http://host
    • wss://host -> https://host
  • Presence read requests (getPresence, getPresenceBatch) are sent to that resolved HTTP base URL