Add signer registry helpers

This commit is contained in:
thePR0M3TH3AN committed 2026-01-18 09:04:12 -05:00
1 parent 0ead956fc5
commit 028d1164f9
3 files changed
+217 -26

No files matched your search

+15 -26
View File
@@ -164,6 +164,12 @@ import {
} from "./nip46Client.js";
import { profileCache } from "../state/profileCache.js";
import { createPrivateKeyCipherClosures } from "./signerHelpers.js";
import {
setActiveSigner as setActiveSignerInRegistry,
getActiveSigner as getActiveSignerFromRegistry,
clearActiveSigner as clearActiveSignerInRegistry,
resolveActiveSigner as resolveActiveSignerFromRegistry,
} from "../nostrClientRegistry.js";
function normalizeProfileFromEvent(event) {
if (!event || !event.content) return null;
@@ -174,9 +180,6 @@ function normalizeProfileFromEvent(event) {
}
}
let activeSigner = null;
const activeSignerRegistry = new Map();
function attachNipMethodAliases(signer) {
if (!signer || typeof signer !== "object") {
return;
@@ -283,45 +286,31 @@ function setActiveSigner(signer) {
hydrateExtensionSignerCapabilities(signer);
attachNipMethodAliases(signer);
activeSigner = signer;
const pubkey =
typeof signer.pubkey === "string" && signer.pubkey.trim()
? signer.pubkey.trim().toLowerCase()
: "";
if (pubkey) {
activeSignerRegistry.set(pubkey, signer);
profileCache.clearSignerRuntime(pubkey);
}
setActiveSignerInRegistry(signer);
}
function getActiveSigner() {
return activeSigner;
const signer = getActiveSignerFromRegistry();
hydrateExtensionSignerCapabilities(signer);
return signer;
}
function clearActiveSigner() {
activeSigner = null;
activeSignerRegistry.clear();
clearActiveSignerInRegistry();
}
function resolveActiveSigner(pubkey) {
if (typeof pubkey === "string" && pubkey.trim()) {
const normalized = pubkey.trim().toLowerCase();
const direct = activeSignerRegistry.get(normalized);
if (direct) {
hydrateExtensionSignerCapabilities(direct);
return direct;
}
if (
activeSigner?.pubkey &&
typeof activeSigner.pubkey === "string" &&
activeSigner.pubkey.trim().toLowerCase() === normalized
) {
hydrateExtensionSignerCapabilities(activeSigner);
return activeSigner;
}
}
hydrateExtensionSignerCapabilities(activeSigner);
return activeSigner;
const signer = resolveActiveSignerFromRegistry(pubkey);
hydrateExtensionSignerCapabilities(signer);
return signer;
}
function shouldRequestExtensionPermissions(signer) {
+10
View File
@@ -9,3 +9,13 @@ export {
nostrClient,
requestDefaultExtensionPermissions,
} from "./nostr/defaultClient.js";
export {
registerSigner,
setActiveSigner,
getActiveSigner,
resolveActiveSigner,
listRegisteredSigners,
onActiveSignerChanged,
offActiveSignerChanged,
} from "./nostrClientRegistry.js";
+192
View File
@@ -2,6 +2,77 @@
let registeredNostrClient = null;
let requestPermissionsDelegate = null;
const registeredSigners = new Map();
let activeSigner = null;
let activeSignerPubkey = "";
const activeSignerListeners = new Set();
const globalScope =
typeof window !== "undefined"
? window
: typeof globalThis !== "undefined"
? globalThis
: null;
function normalizePubkey(pubkey) {
return typeof pubkey === "string" ? pubkey.trim().toLowerCase() : "";
}
function emitSignerEvent(type, detail) {
if (!globalScope || typeof globalScope.dispatchEvent !== "function") {
return;
}
let event = null;
if (typeof globalScope.CustomEvent === "function") {
event = new globalScope.CustomEvent(type, { detail });
} else if (typeof Event === "function") {
event = new Event(type);
event.detail = detail;
}
if (event) {
globalScope.dispatchEvent(event);
}
}
function hasPermissionChanges(previousMeta, nextMeta) {
const previous = previousMeta?.permissions ?? null;
const next = nextMeta?.permissions ?? null;
try {
return JSON.stringify(previous) !== JSON.stringify(next);
} catch (error) {
return previous !== next;
}
}
function updateActiveSigner(nextSigner, nextPubkey) {
const normalizedPubkey = normalizePubkey(nextPubkey);
const previousSigner = activeSigner;
const previousPubkey = activeSignerPubkey;
activeSigner = nextSigner || null;
activeSignerPubkey = normalizedPubkey;
if (
previousSigner !== activeSigner ||
previousPubkey !== activeSignerPubkey
) {
const payload = {
previousSigner,
previousPubkey,
signer: activeSigner,
pubkey: activeSignerPubkey,
};
activeSignerListeners.forEach((listener) => {
try {
listener(payload);
} catch (error) {
// Intentionally swallow listener errors to avoid breaking signer updates.
}
});
}
}
export function registerNostrClient(client, options = {}) {
registeredNostrClient = client || null;
@@ -45,6 +116,127 @@ export function requestDefaultExtensionPermissions(...args) {
return Promise.resolve({ ok: false, error });
}
export function registerSigner(pubkey, signer, meta = {}) {
const normalized = normalizePubkey(pubkey);
if (!normalized) {
return null;
}
const previous = registeredSigners.get(normalized) || null;
if (!signer || typeof signer !== "object") {
if (previous) {
registeredSigners.delete(normalized);
emitSignerEvent("signer:disconnected", {
pubkey: normalized,
signer: previous.signer,
meta: previous.meta,
});
if (activeSignerPubkey === normalized) {
updateActiveSigner(null, "");
}
}
return null;
}
const sanitizedMeta =
meta && typeof meta === "object" ? { ...meta } : {};
registeredSigners.set(normalized, { signer, meta: sanitizedMeta });
if (!previous) {
emitSignerEvent("signer:connected", {
pubkey: normalized,
signer,
meta: sanitizedMeta,
});
} else if (hasPermissionChanges(previous.meta, sanitizedMeta)) {
emitSignerEvent("signer:permissions:changed", {
pubkey: normalized,
signer,
previousMeta: previous.meta,
meta: sanitizedMeta,
});
}
if (activeSignerPubkey === normalized) {
updateActiveSigner(signer, normalized);
}
return signer;
}
export function setActiveSigner(signerOrPubkey) {
if (!signerOrPubkey) {
updateActiveSigner(null, "");
return null;
}
if (typeof signerOrPubkey === "string") {
const normalized = normalizePubkey(signerOrPubkey);
const entry = registeredSigners.get(normalized);
updateActiveSigner(entry?.signer || null, normalized);
return entry?.signer || null;
}
if (typeof signerOrPubkey === "object") {
const pubkey = normalizePubkey(signerOrPubkey.pubkey);
if (pubkey) {
registerSigner(pubkey, signerOrPubkey);
}
updateActiveSigner(signerOrPubkey, pubkey);
return signerOrPubkey;
}
return null;
}
export function clearActiveSigner() {
updateActiveSigner(null, "");
}
export function getActiveSigner() {
return activeSigner;
}
export function resolveActiveSigner(pubkey) {
const normalized = normalizePubkey(pubkey);
if (normalized) {
const entry = registeredSigners.get(normalized);
if (entry?.signer) {
return entry.signer;
}
if (
activeSigner &&
typeof activeSigner.pubkey === "string" &&
normalizePubkey(activeSigner.pubkey) === normalized
) {
return activeSigner;
}
}
return activeSigner;
}
export function listRegisteredSigners() {
return Array.from(registeredSigners.entries()).map(([pubkey, entry]) => ({
pubkey,
signer: entry.signer,
meta: entry.meta,
}));
}
export function onActiveSignerChanged(callback) {
if (typeof callback === "function") {
activeSignerListeners.add(callback);
}
}
export function offActiveSignerChanged(callback) {
activeSignerListeners.delete(callback);
}
export const __nostrClientRegistryTestHooks = {
clearNostrClientRegistration,
clearActiveSigner,
};