From 191ec8d7f087ef778f93a44dddf45b5042294484 Mon Sep 17 00:00:00 2001 From: ennmichael Date: Thu, 2 Mar 2023 23:47:49 +0100 Subject: [PATCH] allow the client to react to ok --- packages/nostr/src/client/conn.ts | 6 +- packages/nostr/src/client/emitter.ts | 39 ++++++++++- packages/nostr/src/client/index.ts | 22 ++++--- packages/nostr/test/simple-communication.ts | 71 ++++++++++++++------- 4 files changed, 102 insertions(+), 36 deletions(-) diff --git a/packages/nostr/src/client/conn.ts b/packages/nostr/src/client/conn.ts index 15963cc..8b24b0b 100644 --- a/packages/nostr/src/client/conn.ts +++ b/packages/nostr/src/client/conn.ts @@ -32,16 +32,16 @@ export class Conn { } constructor({ - endpoint, + url, onMessage, onError, }: { - endpoint: string | URL + url: URL onMessage: (msg: IncomingMessage) => void onError: (err: unknown) => void }) { this.#onError = onError - this.#socket = new WebSocket(endpoint) + this.#socket = new WebSocket(url) // Handle incoming messages. this.#socket.addEventListener("message", async (msgData) => { diff --git a/packages/nostr/src/client/emitter.ts b/packages/nostr/src/client/emitter.ts index f209c0b..2c9eb78 100644 --- a/packages/nostr/src/client/emitter.ts +++ b/packages/nostr/src/client/emitter.ts @@ -1,5 +1,6 @@ import Base from "events" -import { EventParams, Nostr } from "." +import { Nostr, SubscriptionId } from "." +import { EventId, RawEvent, SignedEvent } from "../event" /** * Overrides providing better types for EventEmitter methods. @@ -11,6 +12,7 @@ export class EventEmitter extends Base { listener: RemoveListener ): this override addListener(eventName: "notice", listener: NoticeListener): this + override addListener(eventName: "ok", listener: OkListener): this override addListener(eventName: "error", listener: ErrorListener): this override addListener(eventName: "newListener", listener: ErrorListener): this override addListener(eventName: EventName, listener: Listener): this { @@ -21,6 +23,7 @@ export class EventEmitter extends Base { override emit(eventName: "removeListener", listener: RemoveListener): boolean override emit(eventName: "event", params: EventParams, nostr: Nostr): boolean override emit(eventName: "notice", notice: string, nostr: Nostr): boolean + override emit(eventName: "ok", params: OkParams, nostr: Nostr): boolean override emit(eventName: "error", err: unknown, nostr: Nostr): boolean override emit(eventName: EventName, ...args: unknown[]): boolean { return super.emit(eventName, ...args) @@ -34,6 +37,7 @@ export class EventEmitter extends Base { override listeners(eventName: "removeListener"): EventListener[] override listeners(eventName: "event"): EventListener[] override listeners(eventName: "notice"): NoticeListener[] + override listeners(eventName: "ok"): OkListener[] override listeners(eventName: "error"): ErrorListener[] override listeners(eventName: EventName): Listener[] { return super.listeners(eventName) as Listener[] @@ -43,6 +47,7 @@ export class EventEmitter extends Base { override off(eventName: "removeListener", listener: RemoveListener): this override off(eventName: "event", listener: EventListener): this override off(eventName: "notice", listener: NoticeListener): this + override off(eventName: "ok", listener: OkListener): this override off(eventName: "error", listener: ErrorListener): this override off(eventName: EventName, listener: Listener): this { return super.off(eventName, listener) @@ -52,6 +57,7 @@ export class EventEmitter extends Base { override on(eventName: "removeListener", listener: RemoveListener): this override on(eventName: "event", listener: EventListener): this override on(eventName: "notice", listener: NoticeListener): this + override on(eventName: "ok", listener: OkListener): this override on(eventName: "error", listener: ErrorListener): this override on(eventName: EventName, listener: Listener): this { return super.on(eventName, listener) @@ -61,6 +67,7 @@ export class EventEmitter extends Base { override once(eventName: "removeListener", listener: RemoveListener): this override once(eventName: "event", listener: EventListener): this override once(eventName: "notice", listener: NoticeListener): this + override once(eventName: "ok", listener: OkListener): this override once(eventName: "error", listener: ErrorListener): this override once(eventName: EventName, listener: Listener): this { return super.once(eventName, listener) @@ -76,6 +83,7 @@ export class EventEmitter extends Base { ): this override prependListener(eventName: "event", listener: EventListener): this override prependListener(eventName: "notice", listener: NoticeListener): this + override prependListener(eventName: "ok", listener: OkListener): this override prependListener(eventName: "error", listener: ErrorListener): this override prependListener(eventName: EventName, listener: Listener): this { return super.prependListener(eventName, listener) @@ -97,6 +105,7 @@ export class EventEmitter extends Base { eventName: "notice", listener: NoticeListener ): this + override prependOnceListener(eventName: "ok", listener: OkListener): this override prependOnceListener( eventName: "error", listener: ErrorListener @@ -116,6 +125,7 @@ export class EventEmitter extends Base { ): this override removeListener(eventName: "event", listener: EventListener): this override removeListener(eventName: "notice", listener: NoticeListener): this + override removeListener(eventName: "ok", listener: OkListener): this override removeListener(eventName: "error", listener: ErrorListener): this override removeListener(eventName: EventName, listener: Listener): this { return super.removeListener(eventName, listener) @@ -131,15 +141,40 @@ export class EventEmitter extends Base { // TODO Also add on: ("subscribed", subscriptionId) which checks "OK"/"NOTICE" and makes a callback? // TODO Also add on: ("ok", boolean, eventId) which checks "OK"/"NOTICE" and makes a callback? -type EventName = "newListener" | "removeListener" | "event" | "notice" | "error" +type EventName = + | "newListener" + | "removeListener" + | "event" + | "notice" + | "ok" + | "error" + type NewListener = (eventName: EventName, listener: Listener) => void type RemoveListener = (eventName: EventName, listener: Listener) => void type EventListener = (params: EventParams, nostr: Nostr) => void +type OkListener = (params: OkParams, nostr: Nostr) => void type NoticeListener = (notice: string, nostr: Nostr) => void type ErrorListener = (error: unknown, nostr: Nostr) => void + type Listener = | NewListener | RemoveListener | EventListener | NoticeListener + | OkListener | ErrorListener + +// TODO Document this +export interface EventParams { + signed: SignedEvent + subscriptionId: SubscriptionId + raw: RawEvent +} + +// TODO Document this +export interface OkParams { + eventId: EventId + relay: URL + ok: boolean + message: string +} diff --git a/packages/nostr/src/client/index.ts b/packages/nostr/src/client/index.ts index e6af7bc..d83e189 100644 --- a/packages/nostr/src/client/index.ts +++ b/packages/nostr/src/client/index.ts @@ -46,9 +46,11 @@ export class Nostr extends EventEmitter { return } + const connUrl = new URL(url) + // If there is no existing connection, open a new one. const conn = new Conn({ - endpoint: url, + url: connUrl, // Handle messages on this connection. onMessage: (msg) => { try { @@ -64,6 +66,17 @@ export class Nostr extends EventEmitter { ) } else if (msg.kind === "notice") { this.emit("notice", msg.notice, this) + } else if (msg.kind === "ok") { + this.emit( + "ok", + { + eventId: msg.eventId, + relay: connUrl, + ok: msg.ok, + message: msg.message, + }, + this + ) } else { throw new ProtocolError(`invalid message ${msg}`) } @@ -287,10 +300,3 @@ export interface Filters { until?: Date limit?: number } - -// TODO Document this -export interface EventParams { - signed: SignedEvent - subscriptionId: SubscriptionId - raw: RawEvent -} diff --git a/packages/nostr/test/simple-communication.ts b/packages/nostr/test/simple-communication.ts index 078a898..4841a6e 100644 --- a/packages/nostr/test/simple-communication.ts +++ b/packages/nostr/test/simple-communication.ts @@ -1,26 +1,37 @@ -import { EventParams, Nostr } from "../src/client" -import { EventKind } from "../src/event" +import { Nostr } from "../src/client" +import { EventKind, SignedEvent } from "../src/event" import { PrivateKey } from "../src/keypair" import assert from "assert" +import { EventParams } from "../src/client/emitter" // TODO Switch out the relay implementation and see if the issue persists +// TODO Do on("error", done) for all of these -describe("single event communication", function () { - it("ok", function (done) { - const secret = new PrivateKey( - "nsec1xlu55y6fqfgrq448xslt6a8j2rh7lj08hyhgs94ryq04yf6surwsjl0kzh" - ) - const pubkey = secret.pubkey +describe("simple communication", function () { + const secret = new PrivateKey( + "nsec1xlu55y6fqfgrq448xslt6a8j2rh7lj08hyhgs94ryq04yf6surwsjl0kzh" + ) + const pubkey = secret.pubkey + const timestamp = new Date() + const note = "hello world" + const url = new URL("ws://localhost:12648") - const timestamp = new Date() - const note = "hello world" + const publisher = new Nostr() + const subscriber = new Nostr() - const publisher = new Nostr() - publisher.open("ws://localhost:12648") - const subscriber = new Nostr() - subscriber.open("ws://localhost:12648") + beforeEach(() => { + publisher.open(url) + subscriber.open(url) + }) - function listener({ signed: { event } }: EventParams) { + afterEach(() => { + publisher.close() + subscriber.close() + }) + + it("publish and receive", function (done) { + function listener({ signed: { event } }: EventParams, nostr: Nostr) { + assert.equal(nostr, subscriber) assert.equal(event.kind, EventKind.TextNote) assert.equal(event.pubkey.toString(), pubkey.toString()) assert.equal(event.createdAt.toString(), timestamp.toString()) @@ -34,19 +45,11 @@ describe("single event communication", function () { // for future events. subscriber.off("event", listener) - subscriber.on("error", done) - publisher.on("error", done) - - publisher.close() - subscriber.close() - done() } subscriber.on("event", listener) - subscriber.subscribe([]) - publisher.publish( { kind: EventKind.TextNote, @@ -57,4 +60,26 @@ describe("single event communication", function () { secret ) }) + + it("publish and ok", function (done) { + SignedEvent.sign( + { + kind: EventKind.TextNote, + createdAt: timestamp, + content: note, + pubkey, + }, + secret + ).then((event) => { + publisher.on("ok", (params, nostr) => { + assert.equal(nostr, publisher) + assert.equal(params.eventId.toString(), event.eventId.toString()) + assert.equal(params.relay.toString(), url.toString()) + assert.equal(params.ok, true) + done() + }) + publisher.on("error", done) + publisher.publish(event) + }) + }) })