# 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 ```