From 05ba4e4ef93287536daad6ab18bbfea3cf26d2d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lohrer?= Date: Sat, 18 Apr 2026 05:30:15 +0200 Subject: [PATCH] publish(task 10): nip-46 bunker-signer-wrapper mit timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit createBunkerSigner(bunkerUrl) nutzt NostrConnectSigner.fromBunkerURI aus applesauce-signers 2.x (früher als Nip46Signer im plan bezeichnet; klassenname hat sich geändert). subscription- und publishMethod werden global am class-constructor an einen shared RelayPool gekoppelt. getPublicKey und signEvent bekommen je 30s-timeout mit sauberem clearTimeout via withTimeout-helper. signer.ts ist vollständig, aber ohne eigene unit-tests — integration folgt über check-subcommand (task 16). Co-Authored-By: Claude Opus 4.6 (1M context) --- publish/src/core/signer.ts | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 publish/src/core/signer.ts diff --git a/publish/src/core/signer.ts b/publish/src/core/signer.ts new file mode 100644 index 0000000..94aa04f --- /dev/null +++ b/publish/src/core/signer.ts @@ -0,0 +1,40 @@ +import { NostrConnectSigner } from 'applesauce-signers' +import { RelayPool } from 'applesauce-relay' +import type { UnsignedEvent } from './event.ts' +import type { SignedEvent } from './relays.ts' + +export interface Signer { + getPublicKey(): Promise + signEvent(ev: UnsignedEvent): Promise +} + +const signerPool = new RelayPool() + +NostrConnectSigner.subscriptionMethod = (relays, filters) => signerPool.req(relays, filters) +NostrConnectSigner.publishMethod = (relays, event) => signerPool.event(relays, event) + +function withTimeout(p: Promise, ms: number, label: string): Promise { + let timerId: number | undefined + const timeoutPromise = new Promise((_r, rej) => { + timerId = setTimeout(() => rej(new Error(`${label} timeout`)), ms) + }) + return Promise.race([p, timeoutPromise]).finally(() => { + if (timerId !== undefined) clearTimeout(timerId) + }) as Promise +} + +export async function createBunkerSigner(bunkerUrl: string): Promise { + const signer = await withTimeout( + NostrConnectSigner.fromBunkerURI(bunkerUrl), + 30_000, + 'Bunker connect', + ) + const pubkey = await withTimeout(signer.getPublicKey(), 30_000, 'Bunker getPublicKey') + return { + getPublicKey: () => Promise.resolve(pubkey), + signEvent: async (ev: UnsignedEvent) => { + const signed = await withTimeout(signer.signEvent(ev), 30_000, 'Bunker signEvent') + return signed as SignedEvent + }, + } +}