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

@wisehodl/roots-ws

v0.1.0

Published

Nostr WebSocket transport primitives for TypeScript

Readme

TS-Roots-WS - Nostr WebSocket Transport for TypeScript

Source: https://git.wisehodl.dev/jay/ts-roots-ws

Mirror: https://github.com/wisehodl/ts-roots-ws

What this library does

ts-roots-ws is a consensus-layer Nostr protocol websocket transport library for TypeScript. It only provides primitives for working with Nostr protocol websocket connection states and messages:

  • WebSocket Connection States
  • Envelope Structure
  • Message Validation
  • Protocol Message Creation
  • Protocol Message Parsing
  • Standard Label Handling

What this library does not do

ts-roots-ws serves as a foundation for other libraries and applications to implement higher level transport abstractions on top of it, including:

  • Connection Management
  • Event Loops
  • Subscription Handling
  • State Management
  • Reconnection Logic

Installation

  1. Add ts-roots-ws to your project:
npm install @wisehodl/roots-ws
  1. Import the packages:
import {
  encloseEvent,
  encloseSubscriptionEvent,
  findEvent,
  findSubscriptionEvent,
  getLabel,
  isStandardLabel
} from "@wisehodl/roots-ws/envelope";

import { ConnectionStatus } from "@wisehodl/roots-ws";
import { InvalidJSONError, WrongEnvelopeLabelError } from "@wisehodl/roots-ws/errors";
  1. Access functions with appropriate namespaces.

Usage Examples

Envelope Creation

Create EVENT envelope

// Create an event using ts-roots
import { Event } from "@wisehodl/roots/events";

const event: Event = {
  id: "abc123",
  pubkey: "def456",
  kind: 1,
  content: "Hello Nostr!",
  created_at: Math.floor(Date.now() / 1000),
  tags: [],
  sig: ""
};

// Convert to JSON
const eventJSON = JSON.stringify(event);

// Create envelope
const env = encloseEvent(eventJSON);
// Result: ["EVENT",{"id":"abc123","pubkey":"def456","kind":1,"content":"Hello Nostr!","created_at":1636394097}]

Create subscription EVENT envelope

// Create an event using ts-roots
import { Event } from "@wisehodl/roots/events";

const event: Event = {
  id: "abc123",
  pubkey: "def456",
  kind: 1,
  content: "Hello Nostr!",
  created_at: Math.floor(Date.now() / 1000),
  tags: [],
  sig: ""
};

// Convert to JSON
const eventJSON = JSON.stringify(event);

// Create envelope with subscription ID
const subID = "sub1";
const env = encloseSubscriptionEvent(subID, eventJSON);
// Result: ["EVENT","sub1",{"id":"abc123","pubkey":"def456","kind":1,"content":"Hello Nostr!","created_at":1636394097}]

Create REQ envelope

// Create filters using ts-roots
import { Filter } from "@wisehodl/roots/filters";

const since = Math.floor(Date.now() / 1000) - (24 * 60 * 60);
const limit = 50;

const filter1: Filter = {
  kinds: [1],
  limit: limit,
  since: since
};

const filter2: Filter = {
  authors: ["def456"]
};

// Convert to JSON
const filter1JSON = JSON.stringify(filter1);
const filter2JSON = JSON.stringify(filter2);

// Create envelope
const subID = "sub1";
const filtersJSON = [filter1JSON, filter2JSON];
const env = encloseReq(subID, filtersJSON);
// Result: ["REQ","sub1",{"kinds":[1],"limit":50,"since":1636307697},{"authors":["def456"]}]

Create other envelope types

// Create CLOSE envelope
const env1 = encloseClose("sub1");
// Result: ["CLOSE","sub1"]

// Create EOSE envelope
const env2 = encloseEOSE("sub1");
// Result: ["EOSE","sub1"]

// Create NOTICE envelope
const env3 = encloseNotice("This is a notice");
// Result: ["NOTICE","This is a notice"]

// Create OK envelope
const env4 = encloseOK("abc123", true, "Event accepted");
// Result: ["OK","abc123",true,"Event accepted"]

// Create AUTH challenge
const env5 = encloseAuthChallenge("random-challenge-string");
// Result: ["AUTH","random-challenge-string"]

// Create AUTH response
import { Event } from "@wisehodl/roots/events";

const authEvent: Event = {
  id: "abc123",
  pubkey: "def456",
  kind: 22242,
  content: "",
  created_at: Math.floor(Date.now() / 1000),
  tags: [],
  sig: ""
};

// Convert to JSON
const authEventJSON = JSON.stringify(authEvent);

// Create envelope
const env6 = encloseAuthResponse(authEventJSON);
// Result: ["AUTH",{"id":"abc123","pubkey":"def456","kind":22242,"content":"","created_at":1636394097}]

Envelope Parsing

Extract label from envelope

const env = `["EVENT",{"id":"abc123","pubkey":"def456","kind":1,"content":"Hello Nostr!"}]`;
try {
  const label = getLabel(env);
  // label: "EVENT"

  // Check if label is standard
  const isStandard = isStandardLabel(label);
  // isStandard: true
} catch (err) {
  console.error(err);
}

Extract event from EVENT envelope

const env = `["EVENT",{"id":"abc123","pubkey":"def456","kind":1,"content":"Hello Nostr!"}]`;
try {
  const eventObj = findEvent(env);

  // Parse into ts-roots Event if needed
  import { Event, validate } from "@wisehodl/roots/events";

  // Validate the event
  try {
    validate(eventObj as Event);
  } catch (err) {
    console.error(`Invalid event: ${err.message}`);
  }

  // Now you can access event properties
  console.log(eventObj.id, eventObj.kind, eventObj.content);
} catch (err) {
  console.error(err);
}

Extract subscription event

const env = `["EVENT","sub1",{"id":"abc123","pubkey":"def456","kind":1,"content":"Hello Nostr!"}]`;
try {
  const [subID, eventObj] = findSubscriptionEvent(env);

  // Parse into ts-roots Event if needed
  import { Event } from "@wisehodl/roots/events";

  console.log(`Subscription: ${subID}, Event ID: ${eventObj.id}`);
} catch (err) {
  console.error(err);
}

Extract subscription request

const env = `["REQ","sub1",{"kinds":[1],"limit":50},{"authors":["def456"]}]`;
try {
  const [subID, filtersObj] = findReq(env);

  // Parse each filter
  import { Filter } from "@wisehodl/roots/filters";

  // Now you can use the filter objects
  filtersObj.forEach((filter, i) => {
    console.log(`Filter ${i}: `, filter);
  });
} catch (err) {
  console.error(err);
}

Extract other envelope types

// Extract OK response
const env1 = `["OK","abc123",true,"Event accepted"]`;
try {
  const [eventID, status, message] = findOK(env1);
  // eventID: "abc123"
  // status: true
  // message: "Event accepted"
} catch (err) {
  console.error(err);
}

// Extract EOSE message
const env2 = `["EOSE","sub1"]`;
try {
  const subID = findEOSE(env2);
  // subID: "sub1"
} catch (err) {
  console.error(err);
}

// Extract CLOSE message
const env3 = `["CLOSE","sub1"]`;
try {
  const subID = findClose(env3);
  // subID: "sub1"
} catch (err) {
  console.error(err);
}

// Extract CLOSED message
const env4 = `["CLOSED","sub1","Subscription complete"]`;
try {
  const [subID, message] = findClosed(env4);
  // subID: "sub1"
  // message: "Subscription complete"
} catch (err) {
  console.error(err);
}

// Extract NOTICE message
const env5 = `["NOTICE","This is a notice"]`;
try {
  const message = findNotice(env5);
  // message: "This is a notice"
} catch (err) {
  console.error(err);
}

// Extract AUTH challenge
const env6 = `["AUTH","random-challenge-string"]`;
try {
  const challenge = findAuthChallenge(env6);
  // challenge: "random-challenge-string"
} catch (err) {
  console.error(err);
}

// Extract AUTH response
const env7 = `["AUTH",{"id":"abc123","pubkey":"def456","kind":22242,"content":""}]`;
try {
  const authEvent = findAuthResponse(env7);

  // Parse into ts-roots Event if needed
  import { Event } from "@wisehodl/roots/events";
} catch (err) {
  console.error(err);
}

Testing

This library contains a comprehensive suite of unit tests. Run them with:

npm test

Or for a single run:

npm run test:run