-
Notifications
You must be signed in to change notification settings - Fork 40
fix: NWCClient.close() hanging Jest tests #530
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,8 @@ | ||
| /** @type {import('ts-jest').JestConfigWithTsJest} */ | ||
| module.exports = { | ||
| preset: 'ts-jest', | ||
| testEnvironment: 'node', | ||
| export default { | ||
| preset: "ts-jest", | ||
| testEnvironment: "node", | ||
| transform: { | ||
| "^.+\\.tsx?$": ["ts-jest", { useESM: true }], | ||
| }, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,6 +10,7 @@ import { | |||||||||||
| EventTemplate, | ||||||||||||
| SimplePool, | ||||||||||||
| } from "nostr-tools"; | ||||||||||||
| import { normalizeURL } from "nostr-tools/utils"; | ||||||||||||
| import { hexToBytes, bytesToHex } from "@noble/hashes/utils"; | ||||||||||||
| import { | ||||||||||||
| Nip47EncryptionType, | ||||||||||||
|
|
@@ -55,7 +56,7 @@ import { | |||||||||||
| Nip47CancelHoldInvoiceResponse, | ||||||||||||
| Nip47NetworkError, | ||||||||||||
| } from "./types"; | ||||||||||||
| import { SubCloser } from "nostr-tools/lib/types/abstract-pool"; | ||||||||||||
| import { SubCloser } from "nostr-tools/abstract-pool"; | ||||||||||||
|
|
||||||||||||
| export interface NWCOptions { | ||||||||||||
| relayUrls: string[]; | ||||||||||||
|
|
@@ -198,8 +199,23 @@ export class NWCClient { | |||||||||||
| return getEventHash(event); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| close() { | ||||||||||||
| return this.pool.close(this.relayUrls); | ||||||||||||
|
|
||||||||||||
| async close(): Promise<void> { | ||||||||||||
| const closePromises: Promise<void>[] = []; | ||||||||||||
| for (const url of this.relayUrls) { | ||||||||||||
| const relayUrl = normalizeURL(url); | ||||||||||||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||||||||||||
| const relay = (this.pool as any).relays.get(relayUrl); | ||||||||||||
|
Comment on lines
+207
to
+208
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cat -n src/nwc/NWCClient.ts | sed -n '200,220p'Repository: getAlby/js-sdk Length of output: 841 🏁 Script executed: find . -name "package.json" -type f | head -5 | xargs grep -l "nostr-tools"Repository: getAlby/js-sdk Length of output: 72 🏁 Script executed: cat -n package.json | grep -A 5 -B 5 "nostr-tools"Repository: getAlby/js-sdk Length of output: 439 🌐 Web query:
💡 Result: In nostr-tools / Relay-connection / pool-management API (public surface)Construction / environment
Establishing/keeping relay connections
Closing / cleanupCore “talk to relays” methods you’ll use once connected
If you want, paste your current TypeScript type signature (or your import path: Citations:
🌐 Web query:
💡 Result:
Where it’s documented
What you can do instead (supported)
Docs to start from
Sources: [1] [2] 🏁 Script executed: cat -n src/nwc/NWCClient.ts | sed -n '195,225p'Repository: getAlby/js-sdk Length of output: 1195 🏁 Script executed: grep -n "relayUrls" src/nwc/NWCClient.ts | head -20Repository: getAlby/js-sdk Length of output: 1001 🏁 Script executed: grep -n "this.relayUrls\|this.pool" src/nwc/NWCClient.ts | head -30Repository: getAlby/js-sdk Length of output: 1097 🏁 Script executed: grep -A 5 "listConnectionStatus" src/nwc/NWCClient.tsRepository: getAlby/js-sdk Length of output: 211 🌐 Web query:
💡 Result: In nostr-tools /
Accessing
While Apply the defensive optional-chaining guard at minimum to prevent runtime errors: Defensive guard // eslint-disable-next-line `@typescript-eslint/no-explicit-any`
- const relay = (this.pool as any).relays.get(relayUrl);
+ const relay = (this.pool as any).relays?.get(relayUrl);
+ // NOTE: Accessing internal SimplePool.relays map — will break on nostr-tools upgrades.
if (relay?.ws && relay.ws.readyState !== WebSocket.CLOSED) {Consider opening a feature request upstream with 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
| if (relay?.ws && relay.ws.readyState !== WebSocket.CLOSED) { | ||||||||||||
| closePromises.push( | ||||||||||||
| new Promise<void>((resolve) => { | ||||||||||||
| relay.ws?.addEventListener("close", () => resolve(), { once: true }); | ||||||||||||
| }), | ||||||||||||
| ); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| this.pool.close(this.relayUrls); | ||||||||||||
| await Promise.all(closePromises); | ||||||||||||
|
Comment on lines
+202
to
+218
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing timeout on close promises — could hang indefinitely, reproducing the original bug in a different way. If a remote peer never sends a WebSocket close frame (network issue, unresponsive server), the promises added at Line 211-213 will never resolve, and Add a per-socket timeout that resolves even if the close event never fires: Proposed fix with timeout if (relay?.ws && relay.ws.readyState !== WebSocket.CLOSED) {
closePromises.push(
new Promise<void>((resolve) => {
- relay.ws?.addEventListener("close", () => resolve(), { once: true });
+ const onClose = () => resolve();
+ relay.ws?.addEventListener("close", onClose, { once: true });
+ setTimeout(() => {
+ relay.ws?.removeEventListener("close", onClose);
+ resolve();
+ }, 5000);
}),
);
}🤖 Prompt for AI Agents |
||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| async encrypt(pubkey: string, content: string) { | ||||||||||||
|
|
||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: getAlby/js-sdk
Length of output: 415
🏁 Script executed:
# Double-check if there's a setup file or if jest config has other sections cat -n jest.config.tsRepository: getAlby/js-sdk
Length of output: 305
Add required ESM configuration for Jest.
The
useESM: trueoption requires two additional configurations to work correctly:extensionsToTreatAsEsm: ['.ts']to the jest config to tell Jest to treat.tsfiles as ESM.package.jsonto includeNODE_OPTIONS=--experimental-vm-modules jestor use--experimental-vm-modulesflag. Currently, the test script is just"jest"with no flag, which will cause ESM imports to fail at runtime.Without these additions, tests will fail when encountering ESM imports.
🤖 Prompt for AI Agents