Merge pull request #1680 from PR0M3TH3AN/fix-nip07-login-race-18287588969415200256

Fix NIP-07 login race condition
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-01-25 11:38:08 -05:00
commit 31a03e81b3
3 files changed
+86

No files matched your search

+7
View File
@@ -139,6 +139,7 @@ import {
requestEnablePermissions,
runNip07WithRetry,
writeStoredNip07Permissions,
waitForNip07Extension,
} from "./nip07Permissions.js";
import {
clearStoredSessionActor as clearStoredSessionActorEntry,
@@ -3958,6 +3959,12 @@ export class NostrClient {
*/
async login(options = {}) {
try {
try {
await waitForNip07Extension();
} catch (waitError) {
devLogger.log("Timed out waiting for extension injection:", waitError);
}
const extension = window.nostr;
if (!extension) {
devLogger.log("No Nostr extension found");
+25
View File
@@ -310,3 +310,28 @@ export const __testExports = {
clearStoredNip07Permissions,
normalizePermissionMethod,
};
export function waitForNip07Extension(timeoutMs = 1500) {
return new Promise((resolve, reject) => {
if (typeof window !== "undefined" && window.nostr) {
resolve(window.nostr);
return;
}
if (typeof window === "undefined") {
reject(new Error("Not running in a browser environment."));
return;
}
const start = Date.now();
const interval = setInterval(() => {
if (window.nostr) {
clearInterval(interval);
resolve(window.nostr);
} else if (Date.now() - start > timeoutMs) {
clearInterval(interval);
reject(new Error("Nostr extension not found within timeout."));
}
}, 50);
});
}
+54
View File
@@ -6,6 +6,7 @@ import {
writeStoredNip07Permissions,
clearStoredNip07Permissions,
requestEnablePermissions,
waitForNip07Extension,
} from "../../js/nostr/nip07Permissions.js";
test("writeStoredNip07Permissions normalizes and persists granted methods", () => {
@@ -74,3 +75,56 @@ test("requestEnablePermissions reports unavailable extension", async () => {
assert.strictEqual(result.ok, false);
assert.strictEqual(result.error?.message, "extension-unavailable");
});
test("waitForNip07Extension resolves when extension is present", async () => {
const originalWindow = globalThis.window;
globalThis.window = { nostr: { dummy: true } };
try {
const result = await waitForNip07Extension();
assert.deepStrictEqual(result, { dummy: true });
} finally {
if (originalWindow === undefined) {
delete globalThis.window;
} else {
globalThis.window = originalWindow;
}
}
});
test("waitForNip07Extension resolves when extension appears later", async () => {
const originalWindow = globalThis.window;
globalThis.window = {}; // No nostr yet
setTimeout(() => {
globalThis.window.nostr = { later: true };
}, 50);
try {
const result = await waitForNip07Extension(500);
assert.deepStrictEqual(result, { later: true });
} finally {
if (originalWindow === undefined) {
delete globalThis.window;
} else {
globalThis.window = originalWindow;
}
}
});
test("waitForNip07Extension rejects when extension never appears", async () => {
const originalWindow = globalThis.window;
globalThis.window = {}; // No nostr
try {
await waitForNip07Extension(100);
assert.fail("Should have rejected");
} catch (error) {
assert.strictEqual(error.message, "Nostr extension not found within timeout.");
} finally {
if (originalWindow === undefined) {
delete globalThis.window;
} else {
globalThis.window = originalWindow;
}
}
});