Converted go-roots-ws to typescript.

This commit is contained in:
Jay
2025-11-02 16:16:33 -05:00
commit e84066cae4
18 changed files with 3606 additions and 0 deletions

344
README.md Normal file
View File

@@ -0,0 +1,344 @@
# 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:
```bash
npm install @wisehodl/roots-ws
```
2. Import the packages:
```typescript
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";
```
3. Access functions with appropriate namespaces.
## Usage Examples
### Envelope Creation
#### Create EVENT envelope
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
// 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:
```bash
npm test
```
Or for a single run:
```bash
npm run test:run
```