Remove nip46 broker getter, since websocket connections were getting closed and interrupting login flows

This commit is contained in:
Jon Staab
2025-05-07 11:27:25 -07:00
parent 5aec922dee
commit bfa2ac4f53
4 changed files with 82 additions and 74 deletions
+42 -23
View File
@@ -1,5 +1,5 @@
import {derived} from "svelte/store" import {derived} from "svelte/store"
import {cached, hash, omit, equals, assoc} from "@welshman/lib" import {cached, omit, equals, assoc} from "@welshman/lib"
import {withGetter, synced} from "@welshman/store" import {withGetter, synced} from "@welshman/store"
import { import {
Nip46Broker, Nip46Broker,
@@ -64,7 +64,7 @@ export const pubkey = withGetter(synced<string | undefined>("pubkey", undefined)
export const sessions = withGetter(synced<Record<string, Session>>("sessions", {})) export const sessions = withGetter(synced<Record<string, Session>>("sessions", {}))
export const session = withGetter( export const session = withGetter(
derived([pubkey, sessions], ([$pubkey, $sessions]) => ($pubkey ? $sessions[$pubkey] : null)), derived([pubkey, sessions], ([$pubkey, $sessions]) => ($pubkey ? $sessions[$pubkey] : undefined)),
) )
export const getSession = (pubkey: string) => sessions.get()[pubkey] export const getSession = (pubkey: string) => sessions.get()[pubkey]
@@ -84,13 +84,20 @@ export const updateSession = (pubkey: string, f: (session: Session) => Session)
putSession(f(getSession(pubkey))) putSession(f(getSession(pubkey)))
export const dropSession = (_pubkey: string) => { export const dropSession = (_pubkey: string) => {
const $signer = getSigner.pop(getSession(_pubkey))
if ($signer instanceof Nip46Signer) {
$signer.broker.cleanup()
}
pubkey.update($pubkey => ($pubkey === _pubkey ? undefined : $pubkey)) pubkey.update($pubkey => ($pubkey === _pubkey ? undefined : $pubkey))
sessions.update($sessions => omit([_pubkey], $sessions)) sessions.update($sessions => omit([_pubkey], $sessions))
} }
export const clearSessions = () => { export const clearSessions = () => {
pubkey.set(undefined) for (const pubkey of Object.keys(sessions.get())) {
sessions.set({}) dropSession(pubkey)
}
} }
// Session factories // Session factories
@@ -129,6 +136,23 @@ export const makePubkeySession = (pubkey: string): SessionPubkey => ({
pubkey, pubkey,
}) })
// Type guards
export const isNip01Session = (session?: Session): session is SessionNip01 =>
session?.method === SessionMethod.Nip01
export const isNip07Session = (session?: Session): session is SessionNip07 =>
session?.method === SessionMethod.Nip07
export const isNip46Session = (session?: Session): session is SessionNip46 =>
session?.method === SessionMethod.Nip46
export const isNip55Session = (session?: Session): session is SessionNip55 =>
session?.method === SessionMethod.Nip55
export const isPubkeySession = (session?: Session): session is SessionPubkey =>
session?.method === SessionMethod.Pubkey
// Login utilities // Login utilities
export const loginWithNip01 = (secret: string) => addSession(makeNip01Session(secret)) export const loginWithNip01 = (secret: string) => addSession(makeNip01Session(secret))
@@ -153,25 +177,20 @@ export const nip46Perms = "sign_event:22242,nip04_encrypt,nip04_decrypt,nip44_en
export const getSigner = cached({ export const getSigner = cached({
maxSize: 100, maxSize: 100,
getKey: ([session]: [Session | null]) => hash(String(JSON.stringify(session))), getKey: ([session]: [Session | undefined]) => `${session?.method}:${session?.pubkey}`,
getValue: ([session]: [Session | null]) => { getValue: ([session]: [Session | undefined]) => {
switch (session?.method) { if (isNip07Session(session)) return new Nip07Signer()
case "nip07": if (isNip01Session(session)) return new Nip01Signer(session.secret)
return new Nip07Signer() if (isNip55Session(session)) return new Nip55Signer(session.signer)
case "nip01": if (isNip46Session(session)) {
return new Nip01Signer(session.secret!) const {
case "nip46": secret: clientSecret,
return new Nip46Signer( handler: {relays, pubkey: signerPubkey},
Nip46Broker.get({ } = session
clientSecret: session.secret!, const broker = new Nip46Broker({clientSecret, signerPubkey, relays})
relays: session.handler!.relays, const signer = new Nip46Signer(broker)
signerPubkey: session.handler!.pubkey,
}), return signer
)
case "nip55":
return new Nip55Signer(session.signer!)
default:
return null
} }
}, },
}) })
+15
View File
@@ -35,6 +35,14 @@ export class LRUCache<T, U> {
this.map.delete(this.keys.shift() as T) this.map.delete(this.keys.shift() as T)
} }
} }
pop(k: T) {
const v = this.get(k)
this.map.delete(k)
return v
}
} }
/** /**
@@ -64,9 +72,16 @@ export function cached<T, V, Args extends any[]>({
return cache.get(k)! return cache.get(k)!
} }
const pop = (...args: Args) => {
const k = getKey(args)
return cache.has(k) ? cache.pop(k)! : getValue(args)
}
get.cache = cache get.cache = cache
get.getKey = getKey get.getKey = getKey
get.getValue = getValue get.getValue = getValue
get.pop = pop
return get return get
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/signer", "name": "@welshman/signer",
"version": "0.2.2", "version": "0.2.3",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A nostr signer implemenation supporting several login methods.", "description": "A nostr signer implemenation supporting several login methods.",
+24 -50
View File
@@ -1,13 +1,4 @@
import { import {Emitter, throttle, makePromise, defer, sleep, tryCatch, randomId} from "@welshman/lib"
Emitter,
throttle,
makePromise,
defer,
sleep,
tryCatch,
randomId,
equals,
} from "@welshman/lib"
import { import {
createEvent, createEvent,
normalizeRelayUrl, normalizeRelayUrl,
@@ -19,6 +10,14 @@ import {publish, request, AdapterContext} from "@welshman/net"
import {ISigner, EncryptionImplementation, decrypt, hash, own} from "../util.js" import {ISigner, EncryptionImplementation, decrypt, hash, own} from "../util.js"
import {Nip01Signer} from "./nip01.js" import {Nip01Signer} from "./nip01.js"
export type Nip46Context = {
debug: boolean
}
export const nip46Context = {
debug: false,
}
export type Nip46Algorithm = "nip04" | "nip44" export type Nip46Algorithm = "nip04" | "nip44"
export enum Nip46Event { export enum Nip46Event {
@@ -33,7 +32,6 @@ export type Nip46BrokerParams = {
signerPubkey?: string signerPubkey?: string
algorithm?: Nip46Algorithm algorithm?: Nip46Algorithm
context?: AdapterContext context?: AdapterContext
debug?: (message: string, ...args: any[]) => void
} }
export type Nip46Response = { export type Nip46Response = {
@@ -58,8 +56,6 @@ export type Nip46ResponseWithError = {
error: string error: string
} }
let singleton: Nip46Broker
const popupManager = (() => { const popupManager = (() => {
let pendingUrl = "" let pendingUrl = ""
let pendingSince = 0 let pendingSince = 0
@@ -190,7 +186,9 @@ export class Nip46Sender extends Emitter {
try { try {
await this.send(request) await this.send(request)
} catch (error: any) { } catch (error: any) {
this.params.debug?.("nip46 error:", error, request) if (nip46Context.debug) {
console.log("nip46 error:", error, request)
}
} }
} }
} finally { } finally {
@@ -263,17 +261,6 @@ export class Nip46Broker extends Emitter {
this.receiver = this.makeReceiver() this.receiver = this.makeReceiver()
} }
// Use a static getter to avoid duplicate connections
static get(params: Nip46BrokerParams) {
if (!singleton?.hasParams(params)) {
singleton?.teardown()
singleton = new Nip46Broker(params)
}
return singleton
}
// Static utility methods // Static utility methods
static parseBunkerUrl = (url: string) => { static parseBunkerUrl = (url: string) => {
@@ -294,12 +281,6 @@ export class Nip46Broker extends Emitter {
return {signerPubkey, connectSecret, relays: relays.map(normalizeRelayUrl)} return {signerPubkey, connectSecret, relays: relays.map(normalizeRelayUrl)}
} }
// Expose params without exposing params
hasParams(params: Nip46BrokerParams) {
return equals(this.params, params)
}
// Getters for helper objects // Getters for helper objects
makeSigner = () => new Nip01Signer(this.params.clientSecret) makeSigner = () => new Nip01Signer(this.params.clientSecret)
@@ -307,9 +288,11 @@ export class Nip46Broker extends Emitter {
makeSender = () => { makeSender = () => {
const sender = new Nip46Sender(this.signer, this.params) const sender = new Nip46Sender(this.signer, this.params)
sender.on(Nip46Event.Send, (data: any) => { if (nip46Context.debug) {
this.params.debug?.("nip46 send:", data) sender.on(Nip46Event.Send, (data: any) => {
}) console.log("nip46 send:", data)
})
}
return sender return sender
} }
@@ -317,27 +300,18 @@ export class Nip46Broker extends Emitter {
makeReceiver = () => { makeReceiver = () => {
const receiver = new Nip46Receiver(this.signer, this.params) const receiver = new Nip46Receiver(this.signer, this.params)
receiver.on(Nip46Event.Receive, (data: any) => { if (nip46Context.debug) {
this.params.debug?.("nip46 receive:", data) receiver.on(Nip46Event.Receive, (data: any) => {
}) console.log("nip46 receive:", data)
})
}
return receiver return receiver
} }
// Lifecycle methods // Lifecycle methods
setParams = (params: Partial<Nip46BrokerParams>) => { cleanup = () => {
this.params = {...this.params, ...params}
// Stop everything that's stateful
this.teardown()
// Set it back up again
this.sender = this.makeSender()
this.receiver = this.makeReceiver()
}
teardown = () => {
this.sender.stop() this.sender.stop()
this.receiver.stop() this.receiver.stop()
} }
@@ -382,7 +356,7 @@ export class Nip46Broker extends Emitter {
if (response.result === "auth_url") return if (response.result === "auth_url") return
if (["ack", secret].includes(response.result!)) { if (["ack", secret].includes(response.result!)) {
this.setParams({signerPubkey: response.event.pubkey}) this.params.signerPubkey = response.event.pubkey
if (response.result === "ack") { if (response.result === "ack") {
console.warn( console.warn(