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

@killiandvcz/starling

v1.1.1

Published

> A robust WebSocket client for real-time applications, designed for Helios servers

Readme

🦅 Starling

A robust WebSocket client for real-time applications, designed for Helios servers

Starling is a powerful WebSocket client that provides automatic reconnection, method handling, and seamless peer-to-peer communication through Helios servers. Perfect for building resilient real-time applications, games, and distributed systems.

✨ Features

  • 🔄 Auto-Reconnection - Intelligent reconnection with exponential backoff
  • 🛡️ Bulletproof Reliability - Built-in message acknowledgments and retries
  • 🎯 Method System - Define and call methods like RPC
  • 🔀 Peer-to-Peer - Communicate with other clients through server proxy
  • 📱 Browser & Node - Works everywhere WebSocket is supported
  • 🚀 Promise-Based - Modern async/await API
  • 🔧 Type Safe - Full TypeScript support with JSDoc

🚀 Quick Start

Installation

bun add @killiandvcz/starling
# or
npm install @killiandvcz/starling

Basic Client

import { Starling } from "@killiandvcz/starling";

const starling = new Starling();

// Connect to Helios server
await starling.connect("ws://localhost:3000");

// Call server methods
const user = await starling.request("user:create", {
  name: "John Doe",
  email: "[email protected]"
});

console.log("User created:", user.data);

With Auto-Reconnection

const starling = new Starling();

// Connect with custom options
await starling.connect("ws://localhost:3000", {
  retries: 10,
  timeout: 15000,
  minTimeout: 1000,
  maxTimeout: 30000
});

// Handle connection events
starling.onconnected(() => {
  console.log("🔗 Connected to server");
});

starling.events.on("starling:disconnected", () => {
  console.log("🔌 Disconnected, will auto-reconnect...");
});

📚 Core Concepts

Making Requests

Call server methods and get responses:

// Simple request
const response = await starling.request("ping");
console.log(response.data); // { message: "pong", timestamp: ... }

// Request with payload
const user = await starling.request("user:create", {
  name: "Alice",
  email: "[email protected]"
});

// Request with options
const result = await starling.request("slow-operation", data, {
  timeout: 30000  // 30 second timeout
});

Handling Methods

Your client can also expose methods that others can call:

// Define a method
starling.method("manifest", async (context) => {
  return context.success({
    name: "My App",
    version: "1.0.0",
    description: "A real-time application"
  });
});

// Handle incoming requests
starling.method("process-data", async (context) => {
  const { data } = context.request.payload;
  
  try {
    const result = await processData(data);
    return context.success(result);
  } catch (error) {
    return context.error(error.message);
  }
});

Peer-to-Peer Communication

Communicate with other clients through the server:

// Send message to specific client
const response = await starling.request("get-status", {}, {
  peer: { name: "OtherClient" }
});

// Send data to peer
const result = await starling.request("process-batch", batchData, {
  peer: { service: "DataProcessor" }
});

Sending Messages

Send different types of messages:

// JSON message
starling.json({ type: "notification", message: "Hello!" });

// Text message
starling.text("Simple text message");

// Binary message
const buffer = new Uint8Array([1, 2, 3, 4]);
starling.binary(buffer);

// With options
starling.json(data, {
  topic: "chat",
  tags: ["public", "general"]
});

🔌 API Reference

Starling Class

new Starling()

Creates a new Starling client instance.

starling.connect(url, options?)

Connect to a Helios server.

  • url (string): WebSocket URL (ws:// or wss://)
  • options (object, optional):
    • retries (number): Max retry attempts (default: 5)
    • timeout (number): Connection timeout in ms (default: 10000)
    • minTimeout (number): Min retry delay in ms (default: 1000)
    • maxTimeout (number): Max retry delay in ms (default: 10000)

starling.request(method, payload?, options?)

Make a request to the server or peer.

  • method (string): Method name
  • payload (any, optional): Request data
  • options (object, optional):
    • timeout (number): Request timeout in ms
    • peer (object): Peer routing information

Returns: Promise<Response>

starling.method(name, handler)

Register a method that can be called by server or peers.

  • name (string): Method name
  • handler (function): Async function that receives RequestContext

starling.json(data, options?)

Send JSON message.

starling.text(data, options?)

Send text message.

starling.binary(data, options?)

Send binary message.

starling.close()

Close the connection.

starling.onconnected(callback)

Listen for successful connections.

Events

Listen to connection events:

// Connection established
starling.events.on("starling:connected", (event) => {
  console.log("Connected!", event.data);
});

// Connection lost
starling.events.on("starling:disconnected", (event) => {
  console.log("Disconnected:", event.data);
});

// Connection error
starling.events.on("starling:error", (event) => {
  console.error("Connection error:", event.data);
});

// Message acknowledged
starling.events.on("message:ack", (event) => {
  console.log("Message delivered:", event.data.message.id);
});

🎮 Real-World Examples

Chat Client

const starling = new Starling();
await starling.connect("ws://localhost:3000");

// Join a room
await starling.request("room:join", { roomId: "general" });

// Send messages
document.getElementById("send").onclick = async () => {
  const message = document.getElementById("message").value;
  await starling.request("message:send", { message });
  document.getElementById("message").value = "";
};

// Listen for incoming messages
starling.on("message", (msg) => {
  const messageDiv = document.createElement("div");
  messageDiv.textContent = msg.data.message;
  document.getElementById("messages").appendChild(messageDiv);
});

Game Client

const starling = new Starling();
await starling.connect("ws://localhost:3000");

// Join the game
const player = await starling.request("game:join", {
  playerName: "Alice"
});

// Handle player movement
document.addEventListener("keydown", async (e) => {
  const movement = getMovementFromKey(e.key);
  if (movement) {
    await starling.request("player:move", {
      position: movement
    });
  }
});

// Listen for game state updates
starling.on("gameState", (msg) => {
  updateGameDisplay(msg.data);
});

Service Client

const starling = new Starling();
await starling.connect("ws://localhost:3000");

// Register as a service
starling.method("manifest", async (context) => {
  return context.success({
    name: "ImageProcessor",
    version: "2.1.0",
    capabilities: ["resize", "crop", "filter"]
  });
});

// Handle image processing requests
starling.method("image:process", async (context) => {
  const { image, operations } = context.request.payload;
  
  try {
    const result = await processImage(image, operations);
    return context.success({
      processedImage: result,
      processingTime: Date.now() - startTime
    });
  } catch (error) {
    return context.error(`Processing failed: ${error.message}`);
  }
});

Microservice Communication

const dataService = new Starling();
await dataService.connect("ws://localhost:3000");

// Register as data service
dataService.method("manifest", async (context) => {
  return context.success({ name: "DataService" });
});

// Another service can now call this one
const apiService = new Starling();
await apiService.connect("ws://localhost:3000");

// Call the data service through the server
const userData = await apiService.request("user:get", { id: 123 }, {
  peer: { name: "DataService" }
});

React Integration

import { Starling } from "@killiandvcz/starling";
import { useEffect, useState } from "react";

function useStarling(url) {
  const [starling, setStarling] = useState(null);
  const [connected, setConnected] = useState(false);
  
  useEffect(() => {
    const client = new Starling();
    
    client.connect(url).then(() => {
      setStarling(client);
      setConnected(true);
    });
    
    client.onconnected(() => setConnected(true));
    client.events.on("starling:disconnected", () => setConnected(false));
    
    return () => client.close();
  }, [url]);
  
  return { starling, connected };
}

function ChatApp() {
  const { starling, connected } = useStarling("ws://localhost:3000");
  const [messages, setMessages] = useState([]);
  
  useEffect(() => {
    if (!starling) return;
    
    starling.on("message", (msg) => {
      setMessages(prev => [...prev, msg.data]);
    });
  }, [starling]);
  
  const sendMessage = async (text) => {
    if (starling && connected) {
      await starling.request("message:send", { text });
    }
  };
  
  return (
    <div>
      <div>Status: {connected ? "Connected" : "Disconnected"}</div>
      {/* Chat UI */}
    </div>
  );
}

🔧 Advanced Usage

Custom Message Handling

// Listen to specific message topics
starling.on("notifications", (message) => {
  showNotification(message.data);
});

// Pattern matching
starling.on("user:*", (message) => {
  console.log("User event:", message);
});

// Send with topics
starling.json(data, {
  topic: "notifications",
  tags: ["urgent", "user"]
});

Error Handling

try {
  const result = await starling.request("risky-operation", data);
} catch (error) {
  if (error.message === "Request timed out") {
    // Handle timeout
  } else {
    // Handle other errors
  }
}

// Global error handling
starling.events.on("starling:error", (event) => {
  console.error("Connection error:", event.data);
  // Implement custom error handling
});

Connection State Management

class ConnectionManager {
  constructor(url) {
    this.starling = new Starling();
    this.url = url;
    this.reconnectAttempts = 0;
  }
  
  async connect() {
    try {
      await this.starling.connect(this.url, {
        retries: Infinity, // Never give up
        timeout: 10000
      });
      this.reconnectAttempts = 0;
    } catch (error) {
      this.reconnectAttempts++;
      console.log(`Reconnection attempt ${this.reconnectAttempts} failed`);
      throw error;
    }
  }
  
  isConnected() {
    return this.starling.websocket?.readyState === WebSocket.OPEN;
  }
}

🤝 Related

📄 License

MIT

🙋‍♂️ Support


Built with ❤️ by killiandvcz