Summary
NDKMessenger and NDKConversation both extend EventEmitter from eventemitter3 without type parameters, making all .on(), .off(), and .emit() calls untyped. This means consumers can't get compile-time safety for event names or callback signatures.
Additionally, there's a minor bug in handleIncomingMessage where conversation-created is emitted on every incoming message instead of only on the first message of a new conversation.
Problem
1. Untyped EventEmitter
// Current (src/messenger.ts)
class NDKMessenger extends EventEmitter { ... }
// Consumer gets no type safety:
messenger.on('mesage', (msg) => { ... }) // typo, no error
messenger.on('message', (msg: string) => { ... }) // wrong type, no error
eventemitter3 supports a type map generic that would solve this:
interface NDKMessengerEvents {
'message': (message: NDKMessage) => void;
'conversation-created': (conversation: NDKConversation) => void;
'error': (error: ErrorEvent) => void;
}
class NDKMessenger extends EventEmitter<NDKMessengerEvents> { ... }
Same for NDKConversation:
interface NDKConversationEvents {
'message': (message: NDKMessage) => void;
'error': (error: ErrorEvent) => void;
'state-change': (event: StateChangeEvent) => void;
}
class NDKConversation extends EventEmitter<NDKConversationEvents> { ... }
2. Bug in conversation-created emission
In src/messenger.ts, handleIncomingMessage:
// Current (line ~840)
if (conversation.getMessages.length === 1) {
this.emit("conversation-created", conversation);
}
conversation.getMessages.length reads the function arity (number of parameters of getMessages, which is always 1 because of the limit? param), not the message count. This means conversation-created fires on every incoming message.
Suggested fix:
const messages = await conversation.getMessages();
if (messages.length === 1) {
this.emit("conversation-created", conversation);
}
Workaround
We're currently using module augmentation in our project to add type safety locally:
// messenger-events.d.ts
declare module '@nostr-dev-kit/messages' {
interface NDKMessengerEvents { ... }
interface NDKConversationEvents { ... }
// augment classes to use typed generics
}
Offer
If you agree with these changes, I'd be happy to submit a PR addressing both issues. The scope would be:
- Add event type interfaces (
NDKMessengerEvents, NDKConversationEvents)
- Apply them as generics to
EventEmitter<T>
- Fix the
conversation-created emission logic
- Export the event interfaces for consumers
Let me know!
Summary
NDKMessengerandNDKConversationboth extendEventEmitterfromeventemitter3without type parameters, making all.on(),.off(), and.emit()calls untyped. This means consumers can't get compile-time safety for event names or callback signatures.Additionally, there's a minor bug in
handleIncomingMessagewhereconversation-createdis emitted on every incoming message instead of only on the first message of a new conversation.Problem
1. Untyped EventEmitter
eventemitter3supports a type map generic that would solve this:Same for
NDKConversation:2. Bug in
conversation-createdemissionIn
src/messenger.ts,handleIncomingMessage:conversation.getMessages.lengthreads the function arity (number of parameters ofgetMessages, which is always1because of thelimit?param), not the message count. This meansconversation-createdfires on every incoming message.Suggested fix:
Workaround
We're currently using module augmentation in our project to add type safety locally:
Offer
If you agree with these changes, I'd be happy to submit a PR addressing both issues. The scope would be:
NDKMessengerEvents,NDKConversationEvents)EventEmitter<T>conversation-createdemission logicLet me know!