mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Merge pull request #2268 from PR0M3TH3AN/claude/fix-moderation-lists-error-OcOyU
Add RelayBatchFetcher to NostrClient initialization
This commit is contained in:
3 files changed
+163
-15
No files matched your search
+35
-7
@@ -42,6 +42,7 @@ import {
|
||||
normalizeStoragePointer,
|
||||
resolveStoragePointerValue,
|
||||
} from "../utils/storagePointer.js";
|
||||
import { RelayBatchFetcher } from "./relayBatchFetcher.js";
|
||||
import {
|
||||
createWatchHistoryManager,
|
||||
normalizePointerInput,
|
||||
@@ -218,9 +219,8 @@ import {
|
||||
SignerManager,
|
||||
resolveSignerCapabilities,
|
||||
hydrateExtensionSignerCapabilities,
|
||||
attachNipMethodAliases
|
||||
attachNipMethodAliases,
|
||||
} from "./managers/SignerManager.js";
|
||||
import { RelayBatchFetcher } from "./relayBatchFetcher.js";
|
||||
|
||||
function normalizeProfileFromEvent(event) {
|
||||
if (!event || !event.content) return null;
|
||||
@@ -257,12 +257,8 @@ function setActiveSigner(signer) {
|
||||
if (signer && typeof signer === "object") {
|
||||
hydrateExtensionSignerCapabilities(signer);
|
||||
attachNipMethodAliases(signer);
|
||||
const capsDescriptor = Object.getOwnPropertyDescriptor(
|
||||
signer,
|
||||
"capabilities",
|
||||
);
|
||||
const capsDescriptor = Object.getOwnPropertyDescriptor(signer, "capabilities");
|
||||
const isGetter = capsDescriptor && typeof capsDescriptor.get === "function";
|
||||
|
||||
if (!isGetter) {
|
||||
signer.capabilities = resolveSignerCapabilities(signer);
|
||||
}
|
||||
@@ -800,6 +796,8 @@ export class NostrClient {
|
||||
get sessionActorCipherClosuresPrivateKey() { return this.signerManager.sessionActorCipherClosuresPrivateKey; }
|
||||
set sessionActorCipherClosuresPrivateKey(val) { this.signerManager.sessionActorCipherClosuresPrivateKey = val; }
|
||||
|
||||
get extensionPermissionCache() { return this.signerManager.extensionPermissionCache; }
|
||||
|
||||
get pool() { return this.connectionManager.pool; }
|
||||
set pool(val) { this.connectionManager.pool = val; }
|
||||
|
||||
@@ -2979,9 +2977,39 @@ export class NostrClient {
|
||||
}
|
||||
|
||||
async loginWithExtension(options) {
|
||||
// Check if ensureExtensionPermissions was overridden on this instance (e.g. by tests).
|
||||
// Only proxy when the own property differs from the prototype method to avoid recursion.
|
||||
const ownDesc = Object.getOwnPropertyDescriptor(this, "ensureExtensionPermissions");
|
||||
const isOverridden =
|
||||
ownDesc &&
|
||||
typeof ownDesc.value === "function" &&
|
||||
ownDesc.value !== NostrClient.prototype.ensureExtensionPermissions;
|
||||
|
||||
if (isOverridden) {
|
||||
const sm = this.signerManager;
|
||||
const smOriginal = sm.ensureExtensionPermissions.bind(sm);
|
||||
sm.ensureExtensionPermissions = (...args) => ownDesc.value.call(this, ...args);
|
||||
try {
|
||||
return await sm.loginWithExtension(options);
|
||||
} finally {
|
||||
sm.ensureExtensionPermissions = smOriginal;
|
||||
}
|
||||
}
|
||||
return this.signerManager.loginWithExtension(options);
|
||||
}
|
||||
|
||||
async derivePrivateKeyFromSecret(secret) {
|
||||
return this.signerManager.derivePrivateKeyFromSecret(secret);
|
||||
}
|
||||
|
||||
async registerPrivateKeySigner(params) {
|
||||
return this.signerManager.registerPrivateKeySigner(params);
|
||||
}
|
||||
|
||||
installNip46Client(rpcClient, options) {
|
||||
return this.signerManager.installNip46Client(rpcClient, options);
|
||||
}
|
||||
|
||||
async connectRemoteSigner(params) {
|
||||
return this.signerManager.connectRemoteSigner(params);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import {
|
||||
} from "../nip46LoggingUtils.js";
|
||||
|
||||
import { devLogger, userLogger } from "../../utils/logger.js";
|
||||
import { HEX64_REGEX } from "../../utils/hex.js";
|
||||
import { createPrivateKeyCipherClosures } from "../signerHelpers.js";
|
||||
import { queueSignEvent } from "../signRequestQueue.js";
|
||||
import { signEventWithPrivateKey } from "../publishHelpers.js";
|
||||
@@ -263,13 +264,21 @@ export class SignerManager {
|
||||
this.extensionReady = false;
|
||||
this.extensionPermissionsGranted = false;
|
||||
this.extensionPermissionCache = new Map();
|
||||
const stored = readStoredNip07Permissions();
|
||||
if (stored && stored.size > 0) {
|
||||
this.extensionPermissionsGranted = true;
|
||||
for (const method of stored) {
|
||||
this.extensionPermissionCache.set(method, true);
|
||||
|
||||
// Hydrate permission cache from localStorage so fresh instances
|
||||
// recognise previously granted permissions without re-prompting.
|
||||
try {
|
||||
const storedPermissions = readStoredNip07Permissions();
|
||||
if (storedPermissions.size > 0) {
|
||||
for (const method of storedPermissions) {
|
||||
this.extensionPermissionCache.set(method, true);
|
||||
}
|
||||
this.extensionPermissionsGranted = true;
|
||||
}
|
||||
} catch (_) {
|
||||
// Storage unavailable or corrupt — start with empty cache
|
||||
}
|
||||
|
||||
this.sessionActorCipherClosures = null;
|
||||
this.sessionActorCipherClosuresPrivateKey = null;
|
||||
this.remoteSignerListeners = new Set();
|
||||
@@ -436,8 +445,9 @@ export class SignerManager {
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
let extension;
|
||||
try {
|
||||
await waitForNip07Extension();
|
||||
extension = await waitForNip07Extension();
|
||||
} catch (error) {
|
||||
return { ok: false, error: "extension-missing" };
|
||||
}
|
||||
@@ -552,7 +562,9 @@ export class SignerManager {
|
||||
const adapter = {
|
||||
type: "extension",
|
||||
pubkey: normalized,
|
||||
signEvent: extension.signEvent.bind(extension),
|
||||
signEvent: typeof extension.signEvent === "function"
|
||||
? extension.signEvent.bind(extension)
|
||||
: undefined,
|
||||
nip04: extension.nip04,
|
||||
nip44: extension.nip44,
|
||||
};
|
||||
@@ -941,4 +953,112 @@ export class SignerManager {
|
||||
this.extensionPermissionsGranted = false;
|
||||
this.emitRemoteSignerChange();
|
||||
}
|
||||
|
||||
installNip46Client(rpcClient, { userPubkey } = {}) {
|
||||
this.nip46Client = rpcClient;
|
||||
if (userPubkey) {
|
||||
this.pubkey = userPubkey;
|
||||
}
|
||||
if (rpcClient && typeof rpcClient.getActiveSigner === "function") {
|
||||
const signer = rpcClient.getActiveSigner();
|
||||
if (signer) {
|
||||
this.setActiveSigner(signer);
|
||||
}
|
||||
}
|
||||
this.emitRemoteSignerChange();
|
||||
}
|
||||
|
||||
async derivePrivateKeyFromSecret(secret) {
|
||||
if (!secret || typeof secret !== "string") {
|
||||
throw new Error("A private key or nsec string is required.");
|
||||
}
|
||||
|
||||
const trimmed = secret.trim();
|
||||
const tools = (await ensureNostrTools()) || getCachedNostrTools();
|
||||
|
||||
// Handle hex private key
|
||||
if (HEX64_REGEX.test(trimmed)) {
|
||||
const privateKeyHex = trimmed.toLowerCase();
|
||||
if (!tools || typeof tools.getPublicKey !== "function") {
|
||||
throw new Error("Nostr tools unavailable for key derivation.");
|
||||
}
|
||||
const pubkey = tools.getPublicKey(privateKeyHex);
|
||||
return { privateKey: privateKeyHex, pubkey };
|
||||
}
|
||||
|
||||
// Handle nsec bech32 encoding
|
||||
if (trimmed.startsWith("nsec1")) {
|
||||
const nip19 = tools?.nip19;
|
||||
if (!nip19 || typeof nip19.decode !== "function") {
|
||||
throw new Error("Nostr tools unavailable for nsec decoding.");
|
||||
}
|
||||
const decoded = nip19.decode(trimmed);
|
||||
if (!decoded || decoded.type !== "nsec" || !decoded.data) {
|
||||
throw new Error("Invalid nsec key.");
|
||||
}
|
||||
const secretBytes = decoded.data;
|
||||
const privateKeyHex =
|
||||
typeof secretBytes === "string"
|
||||
? secretBytes
|
||||
: Array.from(secretBytes)
|
||||
.map((b) => b.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
if (!HEX64_REGEX.test(privateKeyHex)) {
|
||||
throw new Error("Decoded nsec key is invalid.");
|
||||
}
|
||||
const pubkey = tools.getPublicKey(privateKeyHex);
|
||||
return { privateKey: privateKeyHex, pubkey };
|
||||
}
|
||||
|
||||
throw new Error("Unrecognized key format. Provide a hex private key or nsec string.");
|
||||
}
|
||||
|
||||
async registerPrivateKeySigner({ privateKey, pubkey, persist, passphrase, validator } = {}) {
|
||||
if (!privateKey || typeof privateKey !== "string" || !HEX64_REGEX.test(privateKey)) {
|
||||
throw new Error("A valid hex private key is required.");
|
||||
}
|
||||
|
||||
const normalizedPrivateKey = privateKey.toLowerCase();
|
||||
const normalizedPubkey =
|
||||
typeof pubkey === "string" && pubkey.trim()
|
||||
? pubkey.trim().toLowerCase()
|
||||
: null;
|
||||
|
||||
if (!normalizedPubkey || !HEX64_REGEX.test(normalizedPubkey)) {
|
||||
throw new Error("A valid hex public key is required.");
|
||||
}
|
||||
|
||||
if (typeof validator === "function") {
|
||||
validator(normalizedPubkey);
|
||||
}
|
||||
|
||||
const cipherClosures = await createPrivateKeyCipherClosures(normalizedPrivateKey);
|
||||
|
||||
const adapter = {
|
||||
type: "nsec",
|
||||
pubkey: normalizedPubkey,
|
||||
signEvent: (event) => signEventWithPrivateKey(event, normalizedPrivateKey),
|
||||
...cipherClosures,
|
||||
};
|
||||
|
||||
this.setActiveSigner(adapter);
|
||||
this.pubkey = normalizedPubkey;
|
||||
|
||||
if (persist && passphrase) {
|
||||
try {
|
||||
const encrypted = await encryptSessionPrivateKey(normalizedPrivateKey, passphrase);
|
||||
persistSessionActorEntry({
|
||||
pubkey: normalizedPubkey,
|
||||
encrypted,
|
||||
source: "nsec",
|
||||
persisted: true,
|
||||
});
|
||||
this.sessionActor = { pubkey: normalizedPubkey, source: "nsec", persisted: true };
|
||||
} catch (error) {
|
||||
devLogger.warn("[nostr] Failed to persist private key signer:", error);
|
||||
}
|
||||
}
|
||||
|
||||
return normalizedPubkey;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ test("ensureActiveSignerForPubkey prefers concurrent login over late extension i
|
||||
const client = new NostrClient();
|
||||
client.pubkey = HEX_PUBKEY;
|
||||
// Pre-condition: Cache permissions to trigger the waitForNip07Extension logic
|
||||
client.extensionPermissionCache.add("sign_event");
|
||||
client.extensionPermissionCache.set("sign_event", true);
|
||||
|
||||
// Mock a NIP-46 signer that might be set during the race
|
||||
const mockNip46Signer = {
|
||||
|
||||
Reference in new issue
Block a user