@qntx/nostr 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{builder-CGgM_QHT.mjs → builder-70CC2EBq.mjs} +4 -4
- package/dist/{builder-CGgM_QHT.mjs.map → builder-70CC2EBq.mjs.map} +1 -1
- package/dist/{builder-C72nJITG.d.mts → builder-Dk00TdNf.d.mts} +2 -2
- package/dist/{client-COkEihj_.mjs → client-BXmvZ7vU.mjs} +45 -30
- package/dist/client-BXmvZ7vU.mjs.map +1 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -2
- package/dist/core.d.mts +8 -7
- package/dist/core.mjs +8 -8
- package/dist/{error-Dyb89ahb.mjs → error-CoP2myne.mjs} +2 -2
- package/dist/{error-Dyb89ahb.mjs.map → error-CoP2myne.mjs.map} +1 -1
- package/dist/{error-BPqAU5RB.d.mts → error-DE1UCZ2J.d.mts} +6 -2
- package/dist/{error-BjSUsx6M.d.mts → error-IHMcLy8-.d.mts} +2 -2
- package/dist/{error-QyECUDIp.mjs → error-VDn7Nv-6.mjs} +2 -2
- package/dist/{error-QyECUDIp.mjs.map → error-VDn7Nv-6.mjs.map} +1 -1
- package/dist/{event-DvM1f03b.mjs → event-DHtcm0jM.mjs} +17 -6
- package/dist/event-DHtcm0jM.mjs.map +1 -0
- package/dist/{filter-3jOh4uLN.mjs → filter-BbShkmc4.mjs} +34 -28
- package/dist/filter-BbShkmc4.mjs.map +1 -0
- package/dist/{filter-oYG2zjJ3.d.mts → filter-w1R-mU63.d.mts} +8 -6
- package/dist/{gossip-BvCItMVZ.d.mts → gossip-C-7WhJ7v.d.mts} +3 -3
- package/dist/{gossip-CRyhK6E6.mjs → gossip-CejMm_QM.mjs} +13 -11
- package/dist/gossip-CejMm_QM.mjs.map +1 -0
- package/dist/gossip.d.mts +1 -1
- package/dist/gossip.mjs +1 -2
- package/dist/{index-B2JlP2Jf.d.mts → index-11znTLM0.d.mts} +4 -4
- package/dist/{index-DzHmhHqD.d.mts → index-B7nMjbyt.d.mts} +3 -3
- package/dist/{index-muydfuYD.d.mts → index-BuNI8JH_.d.mts} +13 -13
- package/dist/{index-Dpj_MUxb.d.mts → index-C5QomO2D.d.mts} +10 -3
- package/dist/{index-DCobP2Jj.d.mts → index-DDZjqV48.d.mts} +10 -10
- package/dist/{index-YID-zPDF.d.mts → index-DYk24u9o.d.mts} +24 -18
- package/dist/index.d.mts +21 -20
- package/dist/index.mjs +19 -19
- package/dist/{key-D_oEutQ6.mjs → key-Bt0ow7mI.mjs} +33 -11
- package/dist/key-Bt0ow7mI.mjs.map +1 -0
- package/dist/{key-DojW2QZl.d.mts → key-E0AJKMRB.d.mts} +19 -11
- package/dist/{loaders-BRh2riV9.mjs → loaders-COGGrZY4.mjs} +7 -7
- package/dist/loaders-COGGrZY4.mjs.map +1 -0
- package/dist/loaders.d.mts +2 -2
- package/dist/loaders.mjs +1 -2
- package/dist/{memory-D_GdEQYG.mjs → memory-Dq4dg-U4.mjs} +2 -2
- package/dist/{memory-D_GdEQYG.mjs.map → memory-Dq4dg-U4.mjs.map} +1 -1
- package/dist/{memory-index-3GSrPIWj.mjs → memory-index-DhlmbqWx.mjs} +19 -18
- package/dist/memory-index-DhlmbqWx.mjs.map +1 -0
- package/dist/{message-BMfreCQc.mjs → message-D6k16gtg.mjs} +20 -20
- package/dist/message-D6k16gtg.mjs.map +1 -0
- package/dist/{verifier-DhAbqVJF.d.mts → message-u_7KOq6Q.d.mts} +3 -22
- package/dist/{nip17-BZvFQLcs.d.mts → nip17--YwFSuph.d.mts} +4 -4
- package/dist/{nip19-D4RKn6GC.d.mts → nip19-qtyc5sCl.d.mts} +2 -2
- package/dist/{nip46-i8L8_nIm.d.mts → nip46-C_ZFQprM.d.mts} +2 -2
- package/dist/{nip51-C_kTYcud.d.mts → nip51-yi9w8FsM.d.mts} +2 -2
- package/dist/{nip59-DfJ6Moeu.d.mts → nip59-B6Ey8L9z.d.mts} +12 -3
- package/dist/{nip65-CPNE0SV0.d.mts → nip65-XrBjV9WM.d.mts} +2 -2
- package/dist/{nip77-DhpoL7d8.d.mts → nip77-BAXijIt_.d.mts} +2 -2
- package/dist/nips/blossom.d.mts +3 -2
- package/dist/nips/blossom.mjs +11 -11
- package/dist/nips/blossom.mjs.map +1 -1
- package/dist/nips/nip04.d.mts +3 -2
- package/dist/nips/nip04.mjs +10 -12
- package/dist/nips/nip04.mjs.map +1 -1
- package/dist/nips/nip05.d.mts +2 -2
- package/dist/nips/nip05.mjs +9 -8
- package/dist/nips/nip05.mjs.map +1 -1
- package/dist/nips/nip10.d.mts +3 -2
- package/dist/nips/nip10.mjs +19 -19
- package/dist/nips/nip10.mjs.map +1 -1
- package/dist/nips/nip11.d.mts +2 -1
- package/dist/nips/nip11.mjs +6 -4
- package/dist/nips/nip11.mjs.map +1 -1
- package/dist/nips/nip13.d.mts +1 -1
- package/dist/nips/nip13.mjs +2 -2
- package/dist/nips/nip13.mjs.map +1 -1
- package/dist/nips/nip17.d.mts +1 -1
- package/dist/nips/nip17.mjs +6 -6
- package/dist/nips/nip17.mjs.map +1 -1
- package/dist/nips/nip19.d.mts +1 -1
- package/dist/nips/nip19.mjs +1 -1
- package/dist/nips/nip21.d.mts +2 -2
- package/dist/nips/nip21.mjs +3 -3
- package/dist/nips/nip21.mjs.map +1 -1
- package/dist/nips/nip27.d.mts +3 -3
- package/dist/nips/nip27.mjs +308 -85
- package/dist/nips/nip27.mjs.map +1 -1
- package/dist/nips/nip42.mjs +1 -1
- package/dist/nips/nip44.d.mts +2 -1
- package/dist/nips/nip44.mjs +4 -4
- package/dist/nips/nip44.mjs.map +1 -1
- package/dist/nips/nip46.d.mts +1 -1
- package/dist/nips/nip46.mjs +9 -8
- package/dist/nips/nip46.mjs.map +1 -1
- package/dist/nips/nip49.d.mts +1 -1
- package/dist/nips/nip49.mjs +1 -1
- package/dist/nips/nip51.d.mts +1 -1
- package/dist/nips/nip51.mjs +14 -14
- package/dist/nips/nip51.mjs.map +1 -1
- package/dist/nips/nip57.mjs +10 -10
- package/dist/nips/nip57.mjs.map +1 -1
- package/dist/nips/nip59.d.mts +1 -1
- package/dist/nips/nip59.mjs +14 -11
- package/dist/nips/nip59.mjs.map +1 -1
- package/dist/nips/nip65.d.mts +1 -1
- package/dist/nips/nip65.mjs +2 -2
- package/dist/nips/nip77.d.mts +1 -1
- package/dist/nips/nip77.mjs +2 -2
- package/dist/nips/nip77.mjs.map +1 -1
- package/dist/nips/nip98.d.mts +1 -1
- package/dist/nips/nip98.mjs +5 -5
- package/dist/nips/nip98.mjs.map +1 -1
- package/dist/{pool-MehgIIzm.d.mts → pool-BMfTQZjM.d.mts} +5 -4
- package/dist/{pool-lMT_mvzn.mjs → pool-D13eVCqc.mjs} +5 -5
- package/dist/{pool-lMT_mvzn.mjs.map → pool-D13eVCqc.mjs.map} +1 -1
- package/dist/{put-BEM5j8xK.mjs → put-DuymazkT.mjs} +5 -5
- package/dist/put-DuymazkT.mjs.map +1 -0
- package/dist/{reactive-B7lPFgTS.d.mts → reactive-BkzOsSwU.d.mts} +3 -3
- package/dist/{reactive-DFHY-f00.mjs → reactive-C0-rTqN9.mjs} +10 -9
- package/dist/reactive-C0-rTqN9.mjs.map +1 -0
- package/dist/{relay-DIR77xQ2.mjs → relay-CbgToreY.mjs} +6 -6
- package/dist/{relay-DIR77xQ2.mjs.map → relay-CbgToreY.mjs.map} +1 -1
- package/dist/relay.d.mts +3 -3
- package/dist/relay.mjs +2 -2
- package/dist/{signer-DJcmC4nA.mjs → signer-DQzjm3pt.mjs} +119 -77
- package/dist/signer-DQzjm3pt.mjs.map +1 -0
- package/dist/signer.d.mts +2 -2
- package/dist/signer.mjs +2 -2
- package/dist/{storage-n2BE1bdY.mjs → storage-DvDAuxzC.mjs} +26 -41
- package/dist/storage-DvDAuxzC.mjs.map +1 -0
- package/dist/storage.d.mts +3 -3
- package/dist/storage.mjs +4 -4
- package/dist/store.d.mts +1 -1
- package/dist/store.mjs +1 -2
- package/dist/testing.d.mts +2 -2
- package/dist/testing.mjs +7 -7
- package/dist/{types-DiVVyf1e.d.mts → types-COupAqtN.d.mts} +2 -2
- package/dist/{types-BJ4wZ7JJ.d.mts → types-DF8yT6EG.d.mts} +7 -1
- package/dist/{util-DfsyJ9nz.mjs → util-BcRBO6o-.mjs} +18 -3
- package/dist/util-BcRBO6o-.mjs.map +1 -0
- package/dist/{verifier-DEHUy4AD.mjs → verifier-DBSPHzGg.mjs} +6 -15
- package/dist/verifier-DBSPHzGg.mjs.map +1 -0
- package/dist/verifier-Or2nDvHb.d.mts +16 -0
- package/package.json +3 -3
- package/dist/client-COkEihj_.mjs.map +0 -1
- package/dist/event-DvM1f03b.mjs.map +0 -1
- package/dist/filter-3jOh4uLN.mjs.map +0 -1
- package/dist/gossip-CRyhK6E6.mjs.map +0 -1
- package/dist/key-D_oEutQ6.mjs.map +0 -1
- package/dist/loaders-BRh2riV9.mjs.map +0 -1
- package/dist/memory-index-3GSrPIWj.mjs.map +0 -1
- package/dist/message-BMfreCQc.mjs.map +0 -1
- package/dist/put-BEM5j8xK.mjs.map +0 -1
- package/dist/reactive-DFHY-f00.mjs.map +0 -1
- package/dist/signer-DJcmC4nA.mjs.map +0 -1
- package/dist/storage-n2BE1bdY.mjs.map +0 -1
- package/dist/util-DfsyJ9nz.mjs.map +0 -1
- package/dist/verifier-DEHUy4AD.mjs.map +0 -1
package/dist/relay.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as RelayClosedError, c as RelayPublishError, i as subscriptionToAsyncIterable, l as RelaySuspendedError, n as Relay, o as RelayConnectionError, r as RelayStatus, s as RelayError, u as RelayTimeoutError } from "./relay-
|
|
2
|
-
import { i as isInsecureRelayUrl, n as fanIn, r as fetchRouted, t as Pool } from "./pool-
|
|
1
|
+
import { a as RelayClosedError, c as RelayPublishError, i as subscriptionToAsyncIterable, l as RelaySuspendedError, n as Relay, o as RelayConnectionError, r as RelayStatus, s as RelayError, u as RelayTimeoutError } from "./relay-CbgToreY.mjs";
|
|
2
|
+
import { i as isInsecureRelayUrl, n as fanIn, r as fetchRouted, t as Pool } from "./pool-D13eVCqc.mjs";
|
|
3
3
|
export { Pool, Relay, RelayClosedError, RelayConnectionError, RelayError, RelayPublishError, RelayStatus, RelaySuspendedError, RelayTimeoutError, fanIn, fetchRouted, isInsecureRelayUrl, subscriptionToAsyncIterable };
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { E as errorMessage, b as EventValidationError, c as isHex32, i as bytesToHex$1, m as nowSeconds, n as assertHex32, s as hexToBytes$1, y as CryptoError } from "./util-BcRBO6o-.mjs";
|
|
2
2
|
import { t as Kind } from "./kind-I2Tvbe82.mjs";
|
|
3
|
-
import { c as markVerified, d as signedMatchesUnsigned, g as Tag, h as validateSignedEvent, m as validateEvent, n as getEventHash } from "./event-
|
|
4
|
-
import {
|
|
5
|
-
import { n as verifyEvent } from "./verifier-
|
|
6
|
-
import { n as SignerDisposedError } from "./error-
|
|
3
|
+
import { c as markVerified, d as signedMatchesUnsigned, g as Tag, h as validateSignedEvent, m as validateEvent, n as getEventHash } from "./event-DHtcm0jM.mjs";
|
|
4
|
+
import { a as nobleBackend, s as signEvent, t as Keys } from "./key-Bt0ow7mI.mjs";
|
|
5
|
+
import { n as verifyEvent } from "./verifier-DBSPHzGg.mjs";
|
|
6
|
+
import { n as SignerDisposedError } from "./error-CoP2myne.mjs";
|
|
7
7
|
import { decrypt, decryptFromPubkey, encrypt, encryptToPubkey } from "./nips/nip04.mjs";
|
|
8
8
|
import { decrypt as decrypt$1, encrypt as encrypt$1, getConversationKey, getConversationKeyFromSharedSecret } from "./nips/nip44.mjs";
|
|
9
|
+
import { unwrap } from "./nips/nip59.mjs";
|
|
9
10
|
import { i as throwIfAborted } from "./abort-DBq2STPj.mjs";
|
|
10
11
|
import { t as invokeSafely } from "./report-D2BM5dgn.mjs";
|
|
11
12
|
import { Nip46Error, decodeNip46Response, encodeNip46Request, parseBunkerURL, parseNostrConnectURI } from "./nips/nip46.mjs";
|
|
@@ -135,8 +136,8 @@ var KeysSigner = class {
|
|
|
135
136
|
#keys;
|
|
136
137
|
#convKeys = /* @__PURE__ */ new Map();
|
|
137
138
|
#disposed = false;
|
|
138
|
-
constructor(secretKey) {
|
|
139
|
-
this.#keys = secretKey instanceof Keys ? secretKey : Keys.fromSecretKey(secretKey);
|
|
139
|
+
constructor(secretKey, options) {
|
|
140
|
+
this.#keys = secretKey instanceof Keys ? secretKey : Keys.fromSecretKey(secretKey, options?.backend ?? nobleBackend);
|
|
140
141
|
}
|
|
141
142
|
/**
|
|
142
143
|
* The live {@link Keys} instance holding the secret material. {@link KeysSigner.dispose} zeroizes
|
|
@@ -160,7 +161,7 @@ var KeysSigner = class {
|
|
|
160
161
|
this.#assertUsable();
|
|
161
162
|
const secret = this.#keys.secretKey.bytes;
|
|
162
163
|
try {
|
|
163
|
-
return encryptToPubkey(plaintext, secret, peer);
|
|
164
|
+
return encryptToPubkey(plaintext, secret, peer, this.#keys.backend);
|
|
164
165
|
} finally {
|
|
165
166
|
secret.fill(0);
|
|
166
167
|
}
|
|
@@ -169,7 +170,7 @@ var KeysSigner = class {
|
|
|
169
170
|
this.#assertUsable();
|
|
170
171
|
const secret = this.#keys.secretKey.bytes;
|
|
171
172
|
try {
|
|
172
|
-
return decryptFromPubkey(ciphertext, secret, peer);
|
|
173
|
+
return decryptFromPubkey(ciphertext, secret, peer, this.#keys.backend);
|
|
173
174
|
} finally {
|
|
174
175
|
secret.fill(0);
|
|
175
176
|
}
|
|
@@ -180,7 +181,7 @@ var KeysSigner = class {
|
|
|
180
181
|
if (!key) {
|
|
181
182
|
const secret = this.#keys.secretKey.bytes;
|
|
182
183
|
try {
|
|
183
|
-
key = getConversationKey(secret, pk);
|
|
184
|
+
key = getConversationKey(secret, pk, this.#keys.backend);
|
|
184
185
|
} finally {
|
|
185
186
|
secret.fill(0);
|
|
186
187
|
}
|
|
@@ -196,6 +197,17 @@ var KeysSigner = class {
|
|
|
196
197
|
this.#assertUsable();
|
|
197
198
|
return decrypt$1(payload, this.#conversationKey(peer));
|
|
198
199
|
}
|
|
200
|
+
/** Unwraps each gift wrap independently — a bad wrap yields an Error, never fails the batch. */
|
|
201
|
+
async unwrapGiftWraps(events) {
|
|
202
|
+
this.#assertUsable();
|
|
203
|
+
const results = [];
|
|
204
|
+
for (const event of events) try {
|
|
205
|
+
results.push(await unwrap(this, event));
|
|
206
|
+
} catch (error) {
|
|
207
|
+
results.push(error instanceof Error ? error : new Error(String(error)));
|
|
208
|
+
}
|
|
209
|
+
return results;
|
|
210
|
+
}
|
|
199
211
|
/**
|
|
200
212
|
* Zeroize the wrapped secret key and every cached NIP-44 conversation key. All later calls reject
|
|
201
213
|
* with {@link SignerDisposedError}. Idempotent.
|
|
@@ -282,9 +294,29 @@ var Nip07Signer = class {
|
|
|
282
294
|
};
|
|
283
295
|
//#endregion
|
|
284
296
|
//#region src/signer/nip46.ts
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Validate `opts.clientSigner` up front — a missing signer or one without NIP-44 cannot run the RPC
|
|
299
|
+
* encryption layer, so construction fails closed with {@link Nip46Error} rather than timing out
|
|
300
|
+
* mid-handshake.
|
|
301
|
+
*/
|
|
302
|
+
function requireClientSigner(opts) {
|
|
303
|
+
const signer = opts.clientSigner;
|
|
304
|
+
if (signer === void 0) throw new Nip46Error("Nip46Signer requires clientSigner");
|
|
305
|
+
const { nip44Encrypt, nip44Decrypt } = signer;
|
|
306
|
+
if (typeof nip44Encrypt !== "function" || typeof nip44Decrypt !== "function") throw new Nip46Error("clientSigner must implement nip44Encrypt and nip44Decrypt");
|
|
307
|
+
return {
|
|
308
|
+
getPublicKey: async () => signer.getPublicKey(),
|
|
309
|
+
signEvent: async (unsigned) => signer.signEvent(unsigned),
|
|
310
|
+
nip44Encrypt: async (peer, plaintext) => nip44Encrypt.call(signer, peer, plaintext),
|
|
311
|
+
nip44Decrypt: async (peer, payload) => nip44Decrypt.call(signer, peer, payload)
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
async function clientPublicKeyOf(signer) {
|
|
315
|
+
const reported = await signer.getPublicKey();
|
|
316
|
+
if (!isHex32(reported)) throw new Nip46Error("remote signer returned an invalid public key");
|
|
317
|
+
const pubkey = reported;
|
|
318
|
+
if (!isHex32(pubkey)) throw new Nip46Error("clientSigner returned an invalid public key");
|
|
319
|
+
return pubkey;
|
|
288
320
|
}
|
|
289
321
|
function resolveTransport(opts) {
|
|
290
322
|
if (opts.pool) return {
|
|
@@ -339,10 +371,9 @@ function resolveBunkerPointer(input) {
|
|
|
339
371
|
var Nip46Signer = class Nip46Signer {
|
|
340
372
|
#pool;
|
|
341
373
|
#ownsPool;
|
|
342
|
-
#
|
|
374
|
+
#clientSigner;
|
|
343
375
|
#clientPubkey;
|
|
344
376
|
#pointer;
|
|
345
|
-
#conversationKey;
|
|
346
377
|
#onAuthUrl;
|
|
347
378
|
#timeoutMs;
|
|
348
379
|
#authTimeoutMs;
|
|
@@ -353,10 +384,10 @@ var Nip46Signer = class Nip46Signer {
|
|
|
353
384
|
#sub;
|
|
354
385
|
#open = false;
|
|
355
386
|
#cachedRemotePubkey;
|
|
356
|
-
constructor(
|
|
387
|
+
constructor(clientSigner, clientPubkey, pointer, pool, ownsPool, opts) {
|
|
357
388
|
if (pointer.relays.length === 0) throw new Nip46Error("bunker pointer has no relays");
|
|
358
|
-
this.#
|
|
359
|
-
this.#clientPubkey =
|
|
389
|
+
this.#clientSigner = clientSigner;
|
|
390
|
+
this.#clientPubkey = clientPubkey;
|
|
360
391
|
this.#pointer = {
|
|
361
392
|
pubkey: pointer.pubkey.toLowerCase(),
|
|
362
393
|
relays: [...pointer.relays],
|
|
@@ -365,7 +396,6 @@ var Nip46Signer = class Nip46Signer {
|
|
|
365
396
|
this.#relays = [...pointer.relays];
|
|
366
397
|
this.#pool = pool;
|
|
367
398
|
this.#ownsPool = ownsPool;
|
|
368
|
-
this.#conversationKey = getConversationKey(clientSecret.bytes, this.#pointer.pubkey);
|
|
369
399
|
this.#onAuthUrl = opts.onAuthUrl;
|
|
370
400
|
this.#timeoutMs = opts.timeoutMs ?? 3e4;
|
|
371
401
|
this.#authTimeoutMs = opts.authTimeoutMs ?? 3e5;
|
|
@@ -384,15 +414,15 @@ var Nip46Signer = class Nip46Signer {
|
|
|
384
414
|
}
|
|
385
415
|
/**
|
|
386
416
|
* Subscribe to an already-known bunker pointer. Does not send the `connect` RPC (reconnect
|
|
387
|
-
* without a new handshake). Requires `
|
|
388
|
-
* identity.
|
|
417
|
+
* without a new handshake). Requires `clientSigner` — reconnect must reuse the original client
|
|
418
|
+
* identity. Async because the client public key comes from the signer.
|
|
389
419
|
*/
|
|
390
|
-
static fromBunker(pointer, opts) {
|
|
391
|
-
|
|
420
|
+
static async fromBunker(pointer, opts) {
|
|
421
|
+
const clientSigner = requireClientSigner(opts);
|
|
422
|
+
const clientPubkey = await clientPublicKeyOf(clientSigner);
|
|
392
423
|
const resolved = applyPointerOpts(pointer, opts);
|
|
393
|
-
const sk = resolveClientSecret(opts.clientSecretKey);
|
|
394
424
|
const { pool, ownsPool } = resolveTransport(opts);
|
|
395
|
-
const signer = new Nip46Signer(
|
|
425
|
+
const signer = new Nip46Signer(clientSigner, clientPubkey, resolved, pool, ownsPool, opts);
|
|
396
426
|
signer.#startSubscription();
|
|
397
427
|
return signer;
|
|
398
428
|
}
|
|
@@ -401,12 +431,9 @@ var Nip46Signer = class Nip46Signer {
|
|
|
401
431
|
* identifier is not a bunker pointer. When both pointer and `opts.relays` are nonempty, unique
|
|
402
432
|
* `opts.relays` are appended.
|
|
403
433
|
*/
|
|
404
|
-
static async connect(input, opts
|
|
434
|
+
static async connect(input, opts) {
|
|
405
435
|
const pointer = resolveBunkerPointer(input);
|
|
406
|
-
const signer = Nip46Signer.fromBunker(pointer,
|
|
407
|
-
...opts,
|
|
408
|
-
clientSecretKey: resolveClientSecret(opts.clientSecretKey)
|
|
409
|
-
});
|
|
436
|
+
const signer = await Nip46Signer.fromBunker(pointer, opts);
|
|
410
437
|
try {
|
|
411
438
|
await signer.connectRemote();
|
|
412
439
|
try {
|
|
@@ -421,17 +448,16 @@ var Nip46Signer = class Nip46Signer {
|
|
|
421
448
|
}
|
|
422
449
|
/**
|
|
423
450
|
* Wait for a bunker to complete a client-initiated `nostrconnect://` handshake. Requires
|
|
424
|
-
* `
|
|
425
|
-
* {@link createNostrConnectURI}
|
|
451
|
+
* `clientSigner` whose pubkey matches the URI client pubkey (build the URI with
|
|
452
|
+
* {@link createNostrConnectURI} and the signer's public key).
|
|
426
453
|
*/
|
|
427
454
|
static async fromNostrConnectURI(connectionURI, opts) {
|
|
428
455
|
throwIfAborted(opts.signal);
|
|
456
|
+
const clientSigner = requireClientSigner(opts);
|
|
429
457
|
const params = parseNostrConnectURI(connectionURI);
|
|
430
|
-
const sk = resolveClientSecret(opts.clientSecretKey);
|
|
431
|
-
const clientPubkey = getPublicKey(sk);
|
|
432
|
-
if (clientPubkey !== params.clientPubkey) throw new Nip46Error("client secret key does not match nostrconnect client pubkey");
|
|
433
458
|
const { pool, ownsPool } = resolveTransport(opts);
|
|
434
459
|
const handshakeTimeoutMs = opts.handshakeTimeoutMs ?? 3e5;
|
|
460
|
+
const clientPubkeyPromise = clientPublicKeyOf(clientSigner);
|
|
435
461
|
return new Promise((resolve, reject) => {
|
|
436
462
|
let settled = false;
|
|
437
463
|
let handshaking = false;
|
|
@@ -449,27 +475,35 @@ var Nip46Signer = class Nip46Signer {
|
|
|
449
475
|
const timer = setTimeout(() => {
|
|
450
476
|
finish(new Nip46Error("nostrconnect handshake timed out"));
|
|
451
477
|
}, handshakeTimeoutMs);
|
|
478
|
+
(async () => {
|
|
479
|
+
try {
|
|
480
|
+
if (await clientPubkeyPromise !== params.clientPubkey) finish(new Nip46Error("clientSigner public key does not match nostrconnect client pubkey"));
|
|
481
|
+
} catch (error) {
|
|
482
|
+
finish(error instanceof Error ? error : new Nip46Error(String(error)));
|
|
483
|
+
}
|
|
484
|
+
})();
|
|
452
485
|
sub = pool.subscribe(params.relays, [{
|
|
453
486
|
kinds: [Kind.NostrConnect],
|
|
454
|
-
"#p": [clientPubkey],
|
|
487
|
+
"#p": [params.clientPubkey],
|
|
455
488
|
limit: 0
|
|
456
489
|
}], {
|
|
457
490
|
signal: opts.signal,
|
|
458
491
|
onEvent: (event) => {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
492
|
+
(async () => {
|
|
493
|
+
try {
|
|
494
|
+
const clientPubkey = await clientPubkeyPromise;
|
|
495
|
+
if (settled || clientPubkey !== params.clientPubkey) return;
|
|
496
|
+
const payload = await clientSigner.nip44Decrypt(event.pubkey, event.content);
|
|
497
|
+
if (decodeNip46Response(payload).result !== params.secret) return;
|
|
498
|
+
if (handshaking) return;
|
|
499
|
+
handshaking = true;
|
|
500
|
+
const pointer = {
|
|
501
|
+
pubkey: event.pubkey.toLowerCase(),
|
|
502
|
+
relays: params.relays,
|
|
503
|
+
secret: params.secret
|
|
504
|
+
};
|
|
505
|
+
const signer = new Nip46Signer(clientSigner, clientPubkey, pointer, pool, ownsPool, opts);
|
|
506
|
+
signer.#startSubscription();
|
|
473
507
|
try {
|
|
474
508
|
await signer.switchRelays();
|
|
475
509
|
} catch {}
|
|
@@ -480,8 +514,8 @@ var Nip46Signer = class Nip46Signer {
|
|
|
480
514
|
await signer.close();
|
|
481
515
|
finish(error instanceof Error ? error : new Nip46Error(String(error)));
|
|
482
516
|
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
517
|
+
} catch {}
|
|
518
|
+
})();
|
|
485
519
|
},
|
|
486
520
|
onClose: (reason) => {
|
|
487
521
|
if (reason !== "connected" && reason !== "failed") finish(new Nip46Error(`nostrconnect subscription closed: ${reason}`));
|
|
@@ -498,23 +532,7 @@ var Nip46Signer = class Nip46Signer {
|
|
|
498
532
|
limit: 0
|
|
499
533
|
}], {
|
|
500
534
|
onEvent: (event) => {
|
|
501
|
-
|
|
502
|
-
const payload = decrypt$1(event.content, this.#conversationKey);
|
|
503
|
-
const { id, result, error } = decodeNip46Response(payload);
|
|
504
|
-
if (result === "auth_url" && this.#listeners.has(id)) {
|
|
505
|
-
const listener = this.#listeners.get(id);
|
|
506
|
-
if (!listener) return;
|
|
507
|
-
if (error !== void 0 && error !== "") invokeSafely(() => this.#onAuthUrl?.(error));
|
|
508
|
-
clearTimeout(listener.timer);
|
|
509
|
-
listener.timer = this.#requestTimer(id, listener, this.#authTimeoutMs);
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
const listener = this.#dropRequest(id);
|
|
513
|
-
if (!listener) return;
|
|
514
|
-
if (error !== void 0 && error !== "") listener.reject(new Nip46Error(error));
|
|
515
|
-
else if (result === void 0) listener.reject(new Nip46Error("empty NIP-46 response"));
|
|
516
|
-
else listener.resolve(result);
|
|
517
|
-
} catch {}
|
|
535
|
+
this.#handleResponseEvent(event);
|
|
518
536
|
},
|
|
519
537
|
onClose: () => {
|
|
520
538
|
this.#sub = void 0;
|
|
@@ -522,6 +540,25 @@ var Nip46Signer = class Nip46Signer {
|
|
|
522
540
|
});
|
|
523
541
|
this.#open = true;
|
|
524
542
|
}
|
|
543
|
+
async #handleResponseEvent(event) {
|
|
544
|
+
try {
|
|
545
|
+
const payload = await this.#clientSigner.nip44Decrypt(event.pubkey, event.content);
|
|
546
|
+
const { id, result, error } = decodeNip46Response(payload);
|
|
547
|
+
if (result === "auth_url" && this.#listeners.has(id)) {
|
|
548
|
+
const listener = this.#listeners.get(id);
|
|
549
|
+
if (!listener) return;
|
|
550
|
+
if (error !== void 0 && error !== "") invokeSafely(() => this.#onAuthUrl?.(error));
|
|
551
|
+
clearTimeout(listener.timer);
|
|
552
|
+
listener.timer = this.#requestTimer(id, listener, this.#authTimeoutMs);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
const listener = this.#dropRequest(id);
|
|
556
|
+
if (!listener) return;
|
|
557
|
+
if (error !== void 0 && error !== "") listener.reject(new Nip46Error(error));
|
|
558
|
+
else if (result === void 0) listener.reject(new Nip46Error("empty NIP-46 response"));
|
|
559
|
+
else listener.resolve(result);
|
|
560
|
+
} catch {}
|
|
561
|
+
}
|
|
525
562
|
async connectRemote(overrides) {
|
|
526
563
|
const metadata = overrides?.metadata ?? this.#metadata;
|
|
527
564
|
const perms = overrides?.perms ?? this.#perms;
|
|
@@ -569,8 +606,8 @@ var Nip46Signer = class Nip46Signer {
|
|
|
569
606
|
async getPublicKey() {
|
|
570
607
|
if (this.#cachedRemotePubkey === void 0) {
|
|
571
608
|
const pk = await this.#sendRequest("get_public_key", []);
|
|
572
|
-
if (!isHex32(pk
|
|
573
|
-
this.#cachedRemotePubkey = pk
|
|
609
|
+
if (!isHex32(pk)) throw new Nip46Error("bunker returned invalid pubkey");
|
|
610
|
+
this.#cachedRemotePubkey = pk;
|
|
574
611
|
}
|
|
575
612
|
return this.#cachedRemotePubkey;
|
|
576
613
|
}
|
|
@@ -619,17 +656,22 @@ var Nip46Signer = class Nip46Signer {
|
|
|
619
656
|
if (!this.#open) throw new Nip46Error("signer is closed");
|
|
620
657
|
if (!this.#sub) this.#startSubscription();
|
|
621
658
|
const id = bytesToHex$1(randomBytes(16));
|
|
622
|
-
const
|
|
659
|
+
const req = {
|
|
623
660
|
id,
|
|
624
661
|
method,
|
|
625
662
|
params
|
|
626
|
-
}
|
|
627
|
-
const
|
|
663
|
+
};
|
|
664
|
+
const encrypted = await this.#clientSigner.nip44Encrypt(this.#pointer.pubkey, encodeNip46Request(req));
|
|
665
|
+
const unsigned = {
|
|
628
666
|
kind: Kind.NostrConnect,
|
|
629
667
|
tags: [Tag.p(this.#pointer.pubkey)],
|
|
630
668
|
content: encrypted,
|
|
631
|
-
created_at: nowSeconds()
|
|
632
|
-
|
|
669
|
+
created_at: nowSeconds(),
|
|
670
|
+
pubkey: this.#clientPubkey
|
|
671
|
+
};
|
|
672
|
+
const event = await this.#clientSigner.signEvent(unsigned);
|
|
673
|
+
if (!verifyEvent(event)) throw new Nip46Error("clientSigner returned an improperly signed request event");
|
|
674
|
+
if (!signedMatchesUnsigned(event, unsigned)) throw new Nip46Error("clientSigner signed an event that does not match the request");
|
|
633
675
|
const resultPromise = new Promise((resolve, reject) => {
|
|
634
676
|
const listener = {
|
|
635
677
|
resolve,
|
|
@@ -670,4 +712,4 @@ var Nip46Signer = class Nip46Signer {
|
|
|
670
712
|
//#endregion
|
|
671
713
|
export { KeysSigner as a, isNip07Available as i, Nip07Signer as n, DelegatedSigner as o, getWindowNostr as r, Nip46Signer as t };
|
|
672
714
|
|
|
673
|
-
//# sourceMappingURL=signer-
|
|
715
|
+
//# sourceMappingURL=signer-DQzjm3pt.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signer-DQzjm3pt.mjs","names":["hexToBytes","bytesToHex","nip44.getConversationKeyFromSharedSecret","nip44.encrypt","nip44.decrypt","nip04.encrypt","nip04.decrypt","nip04.encryptToPubkey","nip04.decryptFromPubkey","nip44.getConversationKey","nip44.encrypt","nip44.decrypt","nip59.unwrap","bytesToHex"],"sources":["../src/signer/delegated.ts","../src/signer/keys.ts","../src/signer/nip07.ts","../src/signer/nip46.ts"],"sourcesContent":["import { schnorr } from \"@noble/curves/secp256k1.js\";\n\nimport { CryptoError, EventValidationError } from \"../core/error.ts\";\nimport type { Event, UnsignedEvent } from \"../core/event.ts\";\nimport { getEventHash, markVerified, validateEvent } from \"../core/event.ts\";\nimport { assertHex32, bytesToHex, hexToBytes, isHex32 } from \"../core/util.ts\";\nimport * as nip04 from \"../nips/nip04.ts\";\nimport * as nip44 from \"../nips/nip44.ts\";\nimport { SignerDisposedError } from \"./error.ts\";\nimport type { NostrSigner } from \"./types.ts\";\n\n/**\n * The three operations an external key holder (wallet keyring, NFC card, platform secure store)\n * provides. The secret itself never enters this process.\n */\nexport type NostrKeyOperations = {\n /** Lowercase 64-char hex x-only public key. */\n getPublicKey: () => Promise<string>;\n /** 64-byte BIP-340 signature over the 32-byte event id. */\n signEventId: (id: Uint8Array) => Promise<Uint8Array>;\n /**\n * Optional. 32-byte x-coordinate of `ECDH(secret, lift_x(peer))`, unhashed. Enables NIP-04 and\n * NIP-44.\n */\n sharedSecret?: (peer: string) => Promise<Uint8Array>;\n};\n\n/**\n * {@link NostrSigner} backed by a {@link NostrKeyOperations} key holder. The signer verifies\n * everything the holder returns: the public key shape, the signature length, and the BIP-340\n * signature itself — a misbehaving holder never produces an event relays would reject.\n */\nexport class DelegatedSigner implements NostrSigner {\n readonly #operations: NostrKeyOperations;\n readonly #convKeys = new Map<string, Uint8Array>();\n #pubkey: string | undefined;\n #disposed = false;\n\n constructor(operations: NostrKeyOperations) {\n this.#operations = operations;\n }\n\n #assertUsable(): void {\n if (this.#disposed) {\n throw new SignerDisposedError(\"signer has been disposed\");\n }\n }\n\n async #publicKey(): Promise<string> {\n let pubkey = this.#pubkey;\n if (pubkey === undefined) {\n pubkey = await this.#operations.getPublicKey();\n this.#assertUsable();\n if (!isHex32(pubkey)) {\n throw new CryptoError(\"key holder returned an invalid public key\");\n }\n this.#pubkey = pubkey;\n }\n return pubkey;\n }\n\n async getPublicKey(): Promise<string> {\n this.#assertUsable();\n return this.#publicKey();\n }\n\n async signEvent(unsigned: UnsignedEvent): Promise<Event> {\n this.#assertUsable();\n if (!validateEvent(unsigned)) {\n throw new EventValidationError(\"cannot sign invalid unsigned event\");\n }\n const pubkey = await this.#publicKey();\n if (unsigned.pubkey !== pubkey) {\n throw new CryptoError(\"unsigned event pubkey does not match the key holder's public key\");\n }\n const id = getEventHash(unsigned);\n const sig = await this.#operations.signEventId(hexToBytes(id));\n this.#assertUsable();\n if (sig.length !== 64) {\n throw new CryptoError(\"key holder returned an invalid signature length\");\n }\n if (!schnorr.verify(sig, hexToBytes(id), hexToBytes(pubkey))) {\n throw new CryptoError(\"key holder returned a signature that fails verification\");\n }\n const event: Event = { ...unsigned, id, sig: bytesToHex(sig) };\n markVerified(event);\n return event;\n }\n\n /**\n * The normalized lowercase peer pubkey is handed to the holder; the returned 32-byte shared\n * secret is the caller's to wipe.\n */\n async #sharedSecret(peer: string): Promise<Uint8Array> {\n const { sharedSecret } = this.#operations;\n if (!sharedSecret) {\n throw new CryptoError(\"key holder does not support ECDH shared secrets\");\n }\n const shared = await sharedSecret(assertHex32(peer, \"peer public key\"));\n if (this.#disposed) {\n shared.fill(0);\n throw new SignerDisposedError(\"signer has been disposed\");\n }\n if (shared.length !== 32) {\n shared.fill(0);\n throw new CryptoError(\"key holder returned an invalid shared secret\");\n }\n return shared;\n }\n\n async #conversationKey(peer: string): Promise<Uint8Array> {\n const pk = assertHex32(peer, \"peer public key\");\n let key = this.#convKeys.get(pk);\n if (!key) {\n const shared = await this.#sharedSecret(pk);\n try {\n key = nip44.getConversationKeyFromSharedSecret(shared);\n } finally {\n shared.fill(0);\n }\n this.#convKeys.set(pk, key);\n }\n return key;\n }\n\n async nip44Encrypt(peer: string, plaintext: string): Promise<string> {\n this.#assertUsable();\n return nip44.encrypt(plaintext, await this.#conversationKey(peer));\n }\n\n async nip44Decrypt(peer: string, payload: string): Promise<string> {\n this.#assertUsable();\n return nip44.decrypt(payload, await this.#conversationKey(peer));\n }\n\n async nip04Encrypt(peer: string, plaintext: string): Promise<string> {\n this.#assertUsable();\n const shared = await this.#sharedSecret(peer);\n try {\n return nip04.encrypt(plaintext, shared);\n } finally {\n shared.fill(0);\n }\n }\n\n async nip04Decrypt(peer: string, ciphertext: string): Promise<string> {\n this.#assertUsable();\n const shared = await this.#sharedSecret(peer);\n try {\n return nip04.decrypt(ciphertext, shared);\n } finally {\n shared.fill(0);\n }\n }\n\n /**\n * Zero-fill and clear every cached NIP-44 conversation key. All later calls reject with\n * {@link SignerDisposedError} and never reach the key holder. Idempotent.\n */\n dispose(): void {\n for (const key of this.#convKeys.values()) {\n key.fill(0);\n }\n this.#convKeys.clear();\n this.#disposed = true;\n }\n}\n","import type { Event, UnsignedEvent } from \"../core/event.ts\";\nimport type { NostrBackend, SecretKeyInput } from \"../core/key.ts\";\nimport { Keys, nobleBackend, signEvent } from \"../core/key.ts\";\nimport * as nip04 from \"../nips/nip04.ts\";\nimport * as nip44 from \"../nips/nip44.ts\";\nimport * as nip59 from \"../nips/nip59.ts\";\nimport type { Rumor } from \"../nips/nip59.ts\";\nimport { SignerDisposedError } from \"./error.ts\";\nimport type { NostrSigner } from \"./types.ts\";\n\n/** Local secret-key signer with NIP-04 (legacy) and NIP-44 support. */\nexport class KeysSigner implements NostrSigner {\n readonly #keys: Keys;\n readonly #convKeys = new Map<string, Uint8Array>();\n #disposed = false;\n\n constructor(secretKey: SecretKeyInput | Keys, options?: { backend?: NostrBackend | undefined }) {\n this.#keys =\n secretKey instanceof Keys\n ? secretKey\n : Keys.fromSecretKey(secretKey, options?.backend ?? nobleBackend);\n }\n\n /**\n * The live {@link Keys} instance holding the secret material. {@link KeysSigner.dispose} zeroizes\n * it, so a reference obtained earlier reports a zeroized secret afterwards.\n */\n get keys(): Keys {\n return this.#keys;\n }\n\n #assertUsable(): void {\n if (this.#disposed) {\n throw new SignerDisposedError(\"signer has been disposed\");\n }\n }\n\n async getPublicKey(): Promise<string> {\n this.#assertUsable();\n return this.#keys.publicKey;\n }\n\n async signEvent(unsigned: UnsignedEvent): Promise<Event> {\n this.#assertUsable();\n return signEvent(unsigned, this.#keys);\n }\n\n async nip04Encrypt(peer: string, plaintext: string): Promise<string> {\n this.#assertUsable();\n const secret = this.#keys.secretKey.bytes;\n try {\n return nip04.encryptToPubkey(plaintext, secret, peer, this.#keys.backend);\n } finally {\n secret.fill(0);\n }\n }\n\n async nip04Decrypt(peer: string, ciphertext: string): Promise<string> {\n this.#assertUsable();\n const secret = this.#keys.secretKey.bytes;\n try {\n return nip04.decryptFromPubkey(ciphertext, secret, peer, this.#keys.backend);\n } finally {\n secret.fill(0);\n }\n }\n\n #conversationKey(peer: string): Uint8Array {\n const pk = peer.toLowerCase();\n let key = this.#convKeys.get(pk);\n if (!key) {\n const secret = this.#keys.secretKey.bytes;\n try {\n key = nip44.getConversationKey(secret, pk, this.#keys.backend);\n } finally {\n secret.fill(0);\n }\n this.#convKeys.set(pk, key);\n }\n return key;\n }\n\n async nip44Encrypt(peer: string, plaintext: string): Promise<string> {\n this.#assertUsable();\n return nip44.encrypt(plaintext, this.#conversationKey(peer));\n }\n\n async nip44Decrypt(peer: string, payload: string): Promise<string> {\n this.#assertUsable();\n return nip44.decrypt(payload, this.#conversationKey(peer));\n }\n\n /** Unwraps each gift wrap independently — a bad wrap yields an Error, never fails the batch. */\n async unwrapGiftWraps(events: ReadonlyArray<Event>): Promise<ReadonlyArray<Rumor | Error>> {\n this.#assertUsable();\n const results: Array<Rumor | Error> = [];\n for (const event of events) {\n try {\n // oxlint-disable-next-line eslint/no-await-in-loop -- per-wrap order is the contract\n results.push(await nip59.unwrap(this, event));\n } catch (error) {\n results.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n return results;\n }\n\n /**\n * Zeroize the wrapped secret key and every cached NIP-44 conversation key. All later calls reject\n * with {@link SignerDisposedError}. Idempotent.\n */\n dispose(): void {\n this.#keys.secretKey.zeroize();\n for (const key of this.#convKeys.values()) {\n key.fill(0);\n }\n this.#convKeys.clear();\n this.#disposed = true;\n }\n}\n","import { CryptoError } from \"../core/error.ts\";\nimport type { Event, EventTemplate, UnsignedEvent } from \"../core/event.ts\";\nimport { signedMatchesUnsigned, validateSignedEvent } from \"../core/event.ts\";\nimport { assertHex32 } from \"../core/util.ts\";\nimport { verifyEvent } from \"../core/verifier.ts\";\nimport type { NostrSigner } from \"./types.ts\";\n\n/**\n * Minimal NIP-07 provider surface (browser extension `window.nostr`).\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/07.md\n */\nexport type WindowNostr = {\n getPublicKey: () => Promise<string>;\n signEvent: (event: EventTemplate) => Promise<Event>;\n nip04?: {\n encrypt: (pubkey: string, plaintext: string) => Promise<string>;\n decrypt: (pubkey: string, ciphertext: string) => Promise<string>;\n };\n nip44?: {\n encrypt: (pubkey: string, plaintext: string) => Promise<string>;\n decrypt: (pubkey: string, ciphertext: string) => Promise<string>;\n };\n};\n\ntype GlobalWithNostr = typeof globalThis & {\n nostr?: WindowNostr;\n window?: { nostr?: WindowNostr };\n};\n\n/** Resolve `window.nostr` / `globalThis.nostr` when present. */\nexport function getWindowNostr(): WindowNostr | undefined {\n const g = globalThis as GlobalWithNostr;\n return g.nostr ?? g.window?.nostr;\n}\n\n/** True when a NIP-07 provider is injected in the current environment. */\nexport function isNip07Available(): boolean {\n const n = getWindowNostr();\n return Boolean(n && typeof n.getPublicKey === \"function\" && typeof n.signEvent === \"function\");\n}\n\n/**\n * Signer backed by a NIP-07 browser extension (or any injected provider). Never holds secret keys\n * in-process.\n */\nexport class Nip07Signer implements NostrSigner {\n readonly #provider: WindowNostr | undefined;\n\n constructor(provider?: WindowNostr) {\n this.#provider = provider;\n }\n\n #resolve(): WindowNostr {\n const provider = this.#provider ?? getWindowNostr();\n if (!provider) {\n throw new CryptoError(\"NIP-07 provider not available (window.nostr missing)\");\n }\n return provider;\n }\n\n async getPublicKey(): Promise<string> {\n const pk = await this.#resolve().getPublicKey();\n try {\n return assertHex32(pk, \"pubkey\");\n } catch (error) {\n throw new CryptoError(\"NIP-07 getPublicKey returned an invalid pubkey\", {\n cause: error,\n });\n }\n }\n\n async signEvent(unsigned: UnsignedEvent): Promise<Event> {\n const provider = this.#resolve();\n const template: EventTemplate = {\n kind: unsigned.kind,\n tags: unsigned.tags.map((t) => {\n const [name, ...rest] = t;\n if (name === undefined) {\n throw new CryptoError(\"unsigned event has an empty tag\");\n }\n return [name, ...rest];\n }),\n content: unsigned.content,\n created_at: unsigned.created_at,\n };\n\n const signed = await provider.signEvent(template);\n if (!validateSignedEvent(signed)) {\n throw new CryptoError(\"NIP-07 signEvent returned an invalid event\");\n }\n if (!verifyEvent(signed)) {\n throw new CryptoError(\"NIP-07 signed event failed signature verification\");\n }\n if (!signedMatchesUnsigned(signed, unsigned)) {\n throw new CryptoError(\"NIP-07 signed event does not match unsigned template\");\n }\n return signed;\n }\n\n async nip04Encrypt(peer: string, plaintext: string): Promise<string> {\n const { nip04 } = this.#resolve();\n if (!nip04?.encrypt) {\n throw new CryptoError(\"NIP-07 provider does not support nip04.encrypt\");\n }\n return nip04.encrypt(peer, plaintext);\n }\n\n async nip04Decrypt(peer: string, ciphertext: string): Promise<string> {\n const { nip04 } = this.#resolve();\n if (!nip04?.decrypt) {\n throw new CryptoError(\"NIP-07 provider does not support nip04.decrypt\");\n }\n return nip04.decrypt(peer, ciphertext);\n }\n\n async nip44Encrypt(peer: string, plaintext: string): Promise<string> {\n const { nip44 } = this.#resolve();\n if (!nip44?.encrypt) {\n throw new CryptoError(\"NIP-07 provider does not support nip44.encrypt\");\n }\n return nip44.encrypt(peer, plaintext);\n }\n\n async nip44Decrypt(peer: string, payload: string): Promise<string> {\n const { nip44 } = this.#resolve();\n if (!nip44?.decrypt) {\n throw new CryptoError(\"NIP-07 provider does not support nip44.decrypt\");\n }\n return nip44.decrypt(peer, payload);\n }\n}\n","import { randomBytes } from \"@noble/hashes/utils.js\";\n\nimport { throwIfAborted } from \"../core/abort.ts\";\nimport { errorMessage } from \"../core/error.ts\";\nimport type { Event, EventTemplate, UnsignedEvent } from \"../core/event.ts\";\nimport { signedMatchesUnsigned, validateSignedEvent } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { invokeSafely } from \"../core/report.ts\";\nimport { Tag } from \"../core/tag.ts\";\nimport { bytesToHex, isHex32, nowSeconds } from \"../core/util.ts\";\nimport { verifyEvent } from \"../core/verifier.ts\";\nimport {\n Nip46Error,\n decodeNip46Response,\n encodeNip46Request,\n parseBunkerURL,\n parseNostrConnectURI,\n} from \"../nips/nip46.ts\";\nimport type { BunkerPointer, ClientMetadata, Nip46Request } from \"../nips/nip46.ts\";\nimport type { NostrSigner } from \"./types.ts\";\n\n/** Subscribe options used by NIP-46 transport (structural subset of Pool). */\nexport type Nip46SubscribeOptions = {\n signal?: AbortSignal | undefined;\n onEvent?: ((event: Event) => void) | undefined;\n onClose?: ((reason: string) => void) | undefined;\n};\n\n/**\n * Structural relay transport for NIP-46 RPC. Satisfied by {@link import(\"../relay/pool.ts\").Pool};\n * constructed above the signer layer so `signer` never imports `relay` (see AGENTS.md).\n */\nexport type Nip46Transport = {\n subscribe: (\n relays: ReadonlyArray<string>,\n filters: ReadonlyArray<Filter>,\n opts?: Nip46SubscribeOptions,\n ) => { close: (reason?: string) => void };\n publish: (\n relays: ReadonlyArray<string>,\n event: Event,\n ) => Promise<\n ReadonlyArray<{\n readonly status: \"ok\" | \"rejected\" | \"failed\";\n readonly message?: string;\n readonly error?: Error;\n }>\n >;\n close: () => void;\n};\n\n/** Options for {@link Nip46Signer}: transport, timeouts, and relay hints. */\nexport type Nip46SignerOptions = {\n /** Shared transport. When set, the signer does not close it on {@link Nip46Signer.close}. */\n pool?: Nip46Transport | undefined;\n /**\n * Factory for a private transport when `pool` is omitted. Typical: `() => new Pool({\n * websocketImplementation, enableReconnect: true })`.\n */\n createPool?: (() => Nip46Transport) | undefined;\n /**\n * Relays used when the bunker pointer has none, and merged uniquely onto a nonempty pointer\n * (pointer first).\n */\n relays?: ReadonlyArray<string> | undefined;\n /** Bunker connection secret when the pointer or bunker:// URL has none. */\n secret?: string | undefined;\n /**\n * Local client identity used to encrypt and sign RPC (not the remote user key). Must implement\n * `nip44Encrypt`/`nip44Decrypt` — the NIP-46 encryption layer is NIP-44. Construction rejects a\n * signer without them.\n */\n clientSigner: NostrSigner;\n /** Called when bunker returns `auth_url` for a pending request. */\n onAuthUrl?: ((url: string) => void) | undefined;\n /** Per-request timeout in ms. Default 30s. */\n timeoutMs?: number | undefined;\n /**\n * Timeout in ms applied after the bunker replies `auth_url`: the request keeps waiting for the\n * real response until this elapses. Default 300s.\n */\n authTimeoutMs?: number | undefined;\n /** Requested permissions sent with `connect` (`method[:kind]` list). */\n perms?: ReadonlyArray<string> | undefined;\n /** Client metadata sent with bunker-initiated `connect`. */\n metadata?: ClientMetadata | undefined;\n};\n\n/** The client-signer surface NIP-46 needs: signing plus the NIP-44 encryption layer. */\ntype ClientSigner = Pick<NostrSigner, \"getPublicKey\" | \"signEvent\"> &\n Required<Pick<NostrSigner, \"nip44Encrypt\" | \"nip44Decrypt\">>;\n\n/**\n * Validate `opts.clientSigner` up front — a missing signer or one without NIP-44 cannot run the RPC\n * encryption layer, so construction fails closed with {@link Nip46Error} rather than timing out\n * mid-handshake.\n */\nfunction requireClientSigner(opts: Nip46SignerOptions): ClientSigner {\n const signer = opts.clientSigner;\n if (signer === undefined) {\n throw new Nip46Error(\"Nip46Signer requires clientSigner\");\n }\n const { nip44Encrypt, nip44Decrypt } = signer;\n if (typeof nip44Encrypt !== \"function\" || typeof nip44Decrypt !== \"function\") {\n throw new Nip46Error(\"clientSigner must implement nip44Encrypt and nip44Decrypt\");\n }\n // Member calls keep the signer's `this` (e.g. KeysSigner private state).\n return {\n getPublicKey: async () => signer.getPublicKey(),\n signEvent: async (unsigned) => signer.signEvent(unsigned),\n nip44Encrypt: async (peer, plaintext) => nip44Encrypt.call(signer, peer, plaintext),\n nip44Decrypt: async (peer, payload) => nip44Decrypt.call(signer, peer, payload),\n };\n}\n\nasync function clientPublicKeyOf(signer: ClientSigner): Promise<string> {\n const reported = await signer.getPublicKey();\n if (!isHex32(reported)) {\n throw new Nip46Error(\"remote signer returned an invalid public key\");\n }\n const pubkey = reported;\n if (!isHex32(pubkey)) {\n throw new Nip46Error(\"clientSigner returned an invalid public key\");\n }\n return pubkey;\n}\n\nfunction resolveTransport(opts: Nip46SignerOptions): { pool: Nip46Transport; ownsPool: boolean } {\n if (opts.pool) {\n return { pool: opts.pool, ownsPool: false };\n }\n if (opts.createPool) {\n return { pool: opts.createPool(), ownsPool: true };\n }\n throw new Nip46Error(\n \"Nip46Signer requires pool or createPool (inject Pool from @qntx/nostr/relay)\",\n );\n}\n\nfunction applyPointerOpts(pointer: BunkerPointer, opts: Nip46SignerOptions): BunkerPointer {\n let next: BunkerPointer = {\n pubkey: pointer.pubkey.toLowerCase(),\n relays: [...pointer.relays],\n secret: pointer.secret,\n };\n if (opts.secret !== undefined && next.secret === undefined) {\n next = { ...next, secret: opts.secret };\n }\n\n const relays = next.relays.length > 0 ? next.relays : [...(opts.relays ?? [])];\n if (relays.length === 0) {\n throw new Nip46Error(\"no relays for bunker connection\");\n }\n if (next.relays.length > 0 && opts.relays !== undefined && opts.relays.length > 0) {\n const merged = [...next.relays];\n for (const r of opts.relays) {\n if (!merged.includes(r)) {\n merged.push(r);\n }\n }\n return { ...next, relays: merged };\n }\n return { ...next, relays };\n}\n\nfunction resolveBunkerPointer(input: string | BunkerPointer): BunkerPointer {\n if (typeof input !== \"string\") {\n return {\n pubkey: input.pubkey.toLowerCase(),\n relays: [...input.relays],\n secret: input.secret,\n };\n }\n\n const bunker = parseBunkerURL(input);\n if (bunker) {\n return bunker;\n }\n\n throw new Nip46Error(\"invalid bunker input (expected bunker:// URL or BunkerPointer)\");\n}\n\ntype PendingRequest = {\n resolve: (v: string) => void;\n reject: (e: Error) => void;\n method: string;\n timer?: ReturnType<typeof setTimeout>;\n};\n\n/**\n * NIP-46 remote signer (bunker / nostrconnect). Implements {@link NostrSigner}; never holds the\n * remote user's secret key. Network I/O is injected via {@link Nip46Transport} (no relay import).\n */\nexport class Nip46Signer implements NostrSigner {\n readonly #pool: Nip46Transport;\n readonly #ownsPool: boolean;\n readonly #clientSigner: ClientSigner;\n readonly #clientPubkey: string;\n readonly #pointer: BunkerPointer;\n readonly #onAuthUrl: ((url: string) => void) | undefined;\n readonly #timeoutMs: number;\n readonly #authTimeoutMs: number;\n readonly #perms: ReadonlyArray<string> | undefined;\n readonly #metadata: ClientMetadata | undefined;\n #relays: string[];\n readonly #listeners = new Map<string, PendingRequest>();\n #sub: { close: (reason?: string) => void } | undefined;\n #open = false;\n #cachedRemotePubkey: string | undefined;\n\n private constructor(\n clientSigner: ClientSigner,\n clientPubkey: string,\n pointer: BunkerPointer,\n pool: Nip46Transport,\n ownsPool: boolean,\n opts: Nip46SignerOptions,\n ) {\n if (pointer.relays.length === 0) {\n throw new Nip46Error(\"bunker pointer has no relays\");\n }\n this.#clientSigner = clientSigner;\n this.#clientPubkey = clientPubkey;\n this.#pointer = {\n pubkey: pointer.pubkey.toLowerCase(),\n relays: [...pointer.relays],\n secret: pointer.secret,\n };\n this.#relays = [...pointer.relays];\n this.#pool = pool;\n this.#ownsPool = ownsPool;\n this.#onAuthUrl = opts.onAuthUrl;\n this.#timeoutMs = opts.timeoutMs ?? 30_000;\n this.#authTimeoutMs = opts.authTimeoutMs ?? 300_000;\n this.#perms = opts.perms;\n this.#metadata = opts.metadata;\n }\n\n get bunker(): Readonly<BunkerPointer> {\n return {\n pubkey: this.#pointer.pubkey,\n relays: [...this.#relays],\n secret: this.#pointer.secret,\n };\n }\n\n get clientPublicKey(): string {\n return this.#clientPubkey;\n }\n\n /**\n * Subscribe to an already-known bunker pointer. Does not send the `connect` RPC (reconnect\n * without a new handshake). Requires `clientSigner` — reconnect must reuse the original client\n * identity. Async because the client public key comes from the signer.\n */\n static async fromBunker(pointer: BunkerPointer, opts: Nip46SignerOptions): Promise<Nip46Signer> {\n const clientSigner = requireClientSigner(opts);\n const clientPubkey = await clientPublicKeyOf(clientSigner);\n const resolved = applyPointerOpts(pointer, opts);\n const { pool, ownsPool } = resolveTransport(opts);\n const signer = new Nip46Signer(clientSigner, clientPubkey, resolved, pool, ownsPool, opts);\n signer.#startSubscription();\n return signer;\n }\n\n /**\n * Connect using a `bunker://` URL or pre-parsed pointer. Invalid strings throw. A NIP-05\n * identifier is not a bunker pointer. When both pointer and `opts.relays` are nonempty, unique\n * `opts.relays` are appended.\n */\n static async connect(\n input: string | BunkerPointer,\n opts: Nip46SignerOptions,\n ): Promise<Nip46Signer> {\n const pointer = resolveBunkerPointer(input);\n const signer = await Nip46Signer.fromBunker(pointer, opts);\n try {\n await signer.connectRemote();\n try {\n await signer.switchRelays();\n } catch {\n // Bunker may not implement switch_relays.\n }\n await signer.getPublicKey();\n return signer;\n } catch (error) {\n await signer.close();\n throw error;\n }\n }\n\n /**\n * Wait for a bunker to complete a client-initiated `nostrconnect://` handshake. Requires\n * `clientSigner` whose pubkey matches the URI client pubkey (build the URI with\n * {@link createNostrConnectURI} and the signer's public key).\n */\n static async fromNostrConnectURI(\n connectionURI: string,\n opts: Nip46SignerOptions & {\n signal?: AbortSignal;\n handshakeTimeoutMs?: number;\n },\n ): Promise<Nip46Signer> {\n throwIfAborted(opts.signal);\n const clientSigner = requireClientSigner(opts);\n const params = parseNostrConnectURI(connectionURI);\n\n const { pool, ownsPool } = resolveTransport(opts);\n const handshakeTimeoutMs = opts.handshakeTimeoutMs ?? 300_000;\n\n // The signer's pubkey resolves async while the handshake subscription is already live —\n // subscribing must stay synchronous so early handshake events are not missed.\n const clientPubkeyPromise = clientPublicKeyOf(clientSigner);\n\n return new Promise((resolve, reject) => {\n let settled = false;\n let handshaking = false;\n // `let` + later assignment: pool.subscribe may fire onClose synchronously (pre-aborted\n // signal via fanIn) before a const initializer would complete.\n // oxlint-disable-next-line eslint/prefer-const\n let sub: { close: (reason?: string) => void } | undefined;\n const finish = (err?: Error, signer?: Nip46Signer) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n sub?.close(err ? \"failed\" : \"connected\");\n if (err) {\n if (ownsPool) {\n pool.close();\n }\n reject(err);\n } else if (signer) {\n resolve(signer);\n }\n };\n\n const timer = setTimeout(() => {\n finish(new Nip46Error(\"nostrconnect handshake timed out\"));\n }, handshakeTimeoutMs);\n\n // The URI's client pubkey gates the handshake: the signer must own that key.\n void (async () => {\n try {\n const pubkey = await clientPubkeyPromise;\n if (pubkey !== params.clientPubkey) {\n finish(\n new Nip46Error(\"clientSigner public key does not match nostrconnect client pubkey\"),\n );\n }\n } catch (error) {\n finish(error instanceof Error ? error : new Nip46Error(String(error)));\n }\n })();\n\n sub = pool.subscribe(\n params.relays,\n [{ kinds: [Kind.NostrConnect], \"#p\": [params.clientPubkey], limit: 0 }],\n {\n signal: opts.signal,\n onEvent: (event) => {\n void (async () => {\n try {\n const clientPubkey = await clientPubkeyPromise;\n if (settled || clientPubkey !== params.clientPubkey) {\n return;\n }\n const payload = await clientSigner.nip44Decrypt(event.pubkey, event.content);\n const response = decodeNip46Response(payload);\n if (response.result !== params.secret) {\n return;\n }\n // A duplicate connect response must not spin up a second signer.\n if (handshaking) {\n return;\n }\n handshaking = true;\n\n const pointer: BunkerPointer = {\n pubkey: event.pubkey.toLowerCase(),\n relays: params.relays,\n secret: params.secret,\n };\n const signer = new Nip46Signer(\n clientSigner,\n clientPubkey,\n pointer,\n pool,\n ownsPool,\n opts,\n );\n signer.#startSubscription();\n try {\n await signer.switchRelays();\n } catch {\n // Relay switch at connect time is best-effort.\n }\n try {\n await signer.getPublicKey();\n finish(undefined, signer);\n } catch (error: unknown) {\n // Do not leak the signer's subscription on a shared pool.\n await signer.close();\n finish(error instanceof Error ? error : new Nip46Error(String(error)));\n }\n } catch {\n // ignore non-matching events\n }\n })();\n },\n onClose: (reason) => {\n if (reason !== \"connected\" && reason !== \"failed\") {\n finish(new Nip46Error(`nostrconnect subscription closed: ${reason}`));\n }\n },\n },\n );\n });\n }\n\n #startSubscription(): void {\n this.#sub?.close();\n this.#sub = this.#pool.subscribe(\n this.#relays,\n [\n {\n kinds: [Kind.NostrConnect],\n authors: [this.#pointer.pubkey],\n \"#p\": [this.#clientPubkey],\n limit: 0,\n },\n ],\n {\n onEvent: (event) => {\n void this.#handleResponseEvent(event);\n },\n onClose: () => {\n this.#sub = undefined;\n },\n },\n );\n this.#open = true;\n }\n\n async #handleResponseEvent(event: Event): Promise<void> {\n try {\n const payload = await this.#clientSigner.nip44Decrypt(event.pubkey, event.content);\n const { id, result, error } = decodeNip46Response(payload);\n\n if (result === \"auth_url\" && this.#listeners.has(id)) {\n const listener = this.#listeners.get(id);\n if (!listener) {\n return;\n }\n if (error !== undefined && error !== \"\") {\n invokeSafely(() => this.#onAuthUrl?.(error));\n }\n clearTimeout(listener.timer);\n listener.timer = this.#requestTimer(id, listener, this.#authTimeoutMs);\n return;\n }\n\n const listener = this.#dropRequest(id);\n if (!listener) {\n return;\n }\n if (error !== undefined && error !== \"\") {\n listener.reject(new Nip46Error(error));\n } else if (result === undefined) {\n listener.reject(new Nip46Error(\"empty NIP-46 response\"));\n } else {\n listener.resolve(result);\n }\n } catch {\n // ignore decrypt failures from unrelated events\n }\n }\n\n async connectRemote(overrides?: {\n metadata?: ClientMetadata | undefined;\n perms?: ReadonlyArray<string> | undefined;\n }): Promise<void> {\n const metadata = overrides?.metadata ?? this.#metadata;\n const perms = overrides?.perms ?? this.#perms;\n const params = [this.#pointer.pubkey, this.#pointer.secret ?? \"\"];\n const hasPerms = perms !== undefined && perms.length > 0;\n if (hasPerms || metadata !== undefined) {\n params.push(hasPerms ? perms.join(\",\") : \"\");\n }\n if (metadata !== undefined) {\n params.push(JSON.stringify(metadata));\n }\n const result = await this.#sendRequest(\"connect\", params);\n if (result === \"ack\") {\n return;\n }\n const { secret } = this.#pointer;\n if (secret !== undefined && secret !== \"\" && result === secret) {\n return;\n }\n throw new Nip46Error(`connect result is not ack or secret: ${result}`);\n }\n\n /**\n * Ask the bunker for its preferred relay list and resubscribe when it changes. Spec result is a\n * JSON array of URLs, or `null` when nothing changes.\n */\n async switchRelays(): Promise<string[] | undefined> {\n const resp = await this.#sendRequest(\"switch_relays\", []);\n if (resp === \"null\") {\n return undefined;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(resp);\n } catch (error) {\n throw new Nip46Error(\"invalid switch_relays JSON\", {\n cause: error,\n });\n }\n if (\n !Array.isArray(parsed) ||\n parsed.length === 0 ||\n !parsed.every((x) => typeof x === \"string\")\n ) {\n throw new Nip46Error(\"invalid switch_relays result\");\n }\n this.#relays = parsed;\n this.#startSubscription();\n return parsed;\n }\n\n /** Courtesy session teardown. Always closes the local signer afterwards. */\n async logout(): Promise<void> {\n try {\n const resp = await this.#sendRequest(\"logout\", []);\n if (resp !== \"ack\") {\n throw new Nip46Error(`logout result is not ack: ${resp}`);\n }\n } finally {\n await this.close();\n }\n }\n\n async ping(): Promise<void> {\n const resp = await this.#sendRequest(\"ping\", []);\n if (resp !== \"pong\") {\n throw new Nip46Error(`ping result is not pong: ${resp}`);\n }\n }\n\n async getPublicKey(): Promise<string> {\n if (this.#cachedRemotePubkey === undefined) {\n const pk = await this.#sendRequest(\"get_public_key\", []);\n if (!isHex32(pk)) {\n throw new Nip46Error(\"bunker returned invalid pubkey\");\n }\n this.#cachedRemotePubkey = pk;\n }\n return this.#cachedRemotePubkey;\n }\n\n async signEvent(unsigned: UnsignedEvent): Promise<Event> {\n const template: EventTemplate = {\n kind: unsigned.kind,\n tags: unsigned.tags,\n content: unsigned.content,\n created_at: unsigned.created_at,\n };\n const resp = await this.#sendRequest(\"sign_event\", [JSON.stringify(template)]);\n let signed: unknown;\n try {\n signed = JSON.parse(resp);\n } catch (error) {\n throw new Nip46Error(\"bunker returned invalid sign_event JSON\", {\n cause: error,\n });\n }\n if (!validateSignedEvent(signed) || !verifyEvent(signed)) {\n throw new Nip46Error(\"bunker returned improperly signed event\");\n }\n if (!signedMatchesUnsigned(signed, unsigned)) {\n throw new Nip46Error(\"signed event does not match unsigned template\");\n }\n return signed;\n }\n\n async nip04Encrypt(peer: string, plaintext: string): Promise<string> {\n return this.#sendRequest(\"nip04_encrypt\", [peer, plaintext]);\n }\n\n async nip04Decrypt(peer: string, ciphertext: string): Promise<string> {\n return this.#sendRequest(\"nip04_decrypt\", [peer, ciphertext]);\n }\n\n async nip44Encrypt(peer: string, plaintext: string): Promise<string> {\n return this.#sendRequest(\"nip44_encrypt\", [peer, plaintext]);\n }\n\n async nip44Decrypt(peer: string, payload: string): Promise<string> {\n return this.#sendRequest(\"nip44_decrypt\", [peer, payload]);\n }\n\n async close(): Promise<void> {\n this.#open = false;\n for (const [, listener] of this.#listeners) {\n clearTimeout(listener.timer);\n listener.reject(new Nip46Error(\"signer closed\"));\n }\n this.#listeners.clear();\n this.#sub?.close(\"signer closed\");\n this.#sub = undefined;\n if (this.#ownsPool) {\n this.#pool.close();\n }\n }\n\n async #sendRequest(method: string, params: string[]): Promise<string> {\n if (!this.#open) {\n throw new Nip46Error(\"signer is closed\");\n }\n if (!this.#sub) {\n this.#startSubscription();\n }\n\n const id = bytesToHex(randomBytes(16));\n const req: Nip46Request = { id, method, params };\n const encrypted = await this.#clientSigner.nip44Encrypt(\n this.#pointer.pubkey,\n encodeNip46Request(req),\n );\n\n const unsigned: UnsignedEvent = {\n kind: Kind.NostrConnect,\n tags: [Tag.p(this.#pointer.pubkey)],\n content: encrypted,\n created_at: nowSeconds(),\n pubkey: this.#clientPubkey,\n };\n const event = await this.#clientSigner.signEvent(unsigned);\n // The client signer is untrusted code: check it actually signed the request we sent.\n if (!verifyEvent(event)) {\n throw new Nip46Error(\"clientSigner returned an improperly signed request event\");\n }\n if (!signedMatchesUnsigned(event, unsigned)) {\n throw new Nip46Error(\"clientSigner signed an event that does not match the request\");\n }\n\n const resultPromise = new Promise<string>((resolve, reject) => {\n const listener: PendingRequest = { resolve, reject, method };\n listener.timer = this.#requestTimer(id, listener, this.#timeoutMs);\n this.#listeners.set(id, listener);\n });\n\n let replies;\n try {\n replies = await this.#pool.publish(this.#relays, event);\n } catch (error) {\n this.#dropRequest(id);\n throw error;\n }\n if (!replies.some((reply) => reply.status === \"ok\")) {\n const listener = this.#dropRequest(id);\n const detail = replies\n .map((reply) => (reply.status === \"failed\" ? errorMessage(reply.error) : reply.message))\n .filter((message): message is string => Boolean(message))\n .join(\"; \");\n listener?.reject(new Nip46Error(`request not accepted by any relay: ${detail}`));\n }\n return resultPromise;\n }\n\n #dropRequest(id: string): PendingRequest | undefined {\n const listener = this.#listeners.get(id);\n if (!listener) {\n return undefined;\n }\n clearTimeout(listener.timer);\n this.#listeners.delete(id);\n return listener;\n }\n\n #requestTimer(id: string, listener: PendingRequest, ms: number): ReturnType<typeof setTimeout> {\n return setTimeout(() => {\n this.#dropRequest(id);\n listener.reject(new Nip46Error(`NIP-46 request timed out: ${listener.method}`));\n }, ms);\n }\n}\n"],"mappings":"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"}
|
package/dist/signer.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as NostrSigner } from "./types-
|
|
2
|
-
import { a as Nip07Signer, c as isNip07Available, d as NostrKeyOperations, f as NoSignerError, i as Nip46Transport, l as KeysSigner, n as Nip46SignerOptions, o as WindowNostr, p as SignerDisposedError, r as Nip46SubscribeOptions, s as getWindowNostr, t as Nip46Signer, u as DelegatedSigner } from "./index-
|
|
1
|
+
import { t as NostrSigner } from "./types-DF8yT6EG.mjs";
|
|
2
|
+
import { a as Nip07Signer, c as isNip07Available, d as NostrKeyOperations, f as NoSignerError, i as Nip46Transport, l as KeysSigner, n as Nip46SignerOptions, o as WindowNostr, p as SignerDisposedError, r as Nip46SubscribeOptions, s as getWindowNostr, t as Nip46Signer, u as DelegatedSigner } from "./index-DYk24u9o.mjs";
|
|
3
3
|
export { DelegatedSigner, KeysSigner, Nip07Signer, Nip46Signer, type Nip46SignerOptions, type Nip46SubscribeOptions, type Nip46Transport, NoSignerError, type NostrKeyOperations, type NostrSigner, SignerDisposedError, type WindowNostr, getWindowNostr, isNip07Available };
|
package/dist/signer.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as SignerDisposedError, t as NoSignerError } from "./error-
|
|
2
|
-
import { a as KeysSigner, i as isNip07Available, n as Nip07Signer, o as DelegatedSigner, r as getWindowNostr, t as Nip46Signer } from "./signer-
|
|
1
|
+
import { n as SignerDisposedError, t as NoSignerError } from "./error-CoP2myne.mjs";
|
|
2
|
+
import { a as KeysSigner, i as isNip07Available, n as Nip07Signer, o as DelegatedSigner, r as getWindowNostr, t as Nip46Signer } from "./signer-DQzjm3pt.mjs";
|
|
3
3
|
export { DelegatedSigner, KeysSigner, Nip07Signer, Nip46Signer, NoSignerError, SignerDisposedError, getWindowNostr, isNip07Available };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { u as isRecord } from "./util-
|
|
1
|
+
import { u as isRecord } from "./util-BcRBO6o-.mjs";
|
|
2
2
|
import { t as Kind } from "./kind-I2Tvbe82.mjs";
|
|
3
|
-
import { S as parseEventAddress, _ as eventAddress, f as sortEvents, h as validateSignedEvent, o as itemCompare, t as compareEventsDesc, y as formatEventAddress } from "./event-
|
|
4
|
-
import { o as matchFilter } from "./filter-
|
|
5
|
-
import { n as toStorageError, t as StorageError } from "./error-
|
|
6
|
-
import { i as DeletionState, n as decidePut, r as outboxBoundKey } from "./put-
|
|
3
|
+
import { S as parseEventAddress, _ as eventAddress, f as sortEvents, h as validateSignedEvent, o as itemCompare, t as compareEventsDesc, y as formatEventAddress } from "./event-DHtcm0jM.mjs";
|
|
4
|
+
import { i as filterTagConditions, o as matchFilter, t as canonicalizeFilter } from "./filter-BbShkmc4.mjs";
|
|
5
|
+
import { n as toStorageError, t as StorageError } from "./error-VDn7Nv-6.mjs";
|
|
6
|
+
import { i as DeletionState, n as decidePut, r as outboxBoundKey } from "./put-DuymazkT.mjs";
|
|
7
7
|
import { t as SerialQueue } from "./serial-Bzpw-xz3.mjs";
|
|
8
8
|
//#region src/storage/idb-types.ts
|
|
9
9
|
const EVENTS = "events";
|
|
@@ -673,11 +673,12 @@ var IndexedDbEventStore = class IndexedDbEventStore {
|
|
|
673
673
|
return event;
|
|
674
674
|
}
|
|
675
675
|
async query(filters) {
|
|
676
|
+
const canonical = filters.map(canonicalizeFilter);
|
|
676
677
|
const tx = (await this.#ensure()).transaction([EVENTS, TAG_REFS], "readonly");
|
|
677
678
|
const done = txDone(tx);
|
|
678
679
|
const seen = /* @__PURE__ */ new Set();
|
|
679
680
|
const events = [];
|
|
680
|
-
for (const filter of
|
|
681
|
+
for (const filter of canonical) {
|
|
681
682
|
const matched = await this.#queryOne(tx, filter);
|
|
682
683
|
for (const event of matched) {
|
|
683
684
|
if (seen.has(event.id)) continue;
|
|
@@ -690,10 +691,11 @@ var IndexedDbEventStore = class IndexedDbEventStore {
|
|
|
690
691
|
return events;
|
|
691
692
|
}
|
|
692
693
|
async count(filters) {
|
|
694
|
+
const canonical = filters.map(canonicalizeFilter);
|
|
693
695
|
const tx = (await this.#ensure()).transaction([EVENTS, TAG_REFS], "readonly");
|
|
694
696
|
const done = txDone(tx);
|
|
695
697
|
const seen = /* @__PURE__ */ new Set();
|
|
696
|
-
for (const filter of
|
|
698
|
+
for (const filter of canonical) {
|
|
697
699
|
const local = /* @__PURE__ */ new Set();
|
|
698
700
|
await this.#scan(tx, filter, (event) => {
|
|
699
701
|
local.add(event.id);
|
|
@@ -705,15 +707,16 @@ var IndexedDbEventStore = class IndexedDbEventStore {
|
|
|
705
707
|
return seen.size;
|
|
706
708
|
}
|
|
707
709
|
async negentropyItems(filter) {
|
|
710
|
+
const canonical = canonicalizeFilter(filter);
|
|
708
711
|
const tx = (await this.#ensure()).transaction([EVENTS, TAG_REFS], "readonly");
|
|
709
712
|
const done = txDone(tx);
|
|
710
713
|
const items = [];
|
|
711
|
-
await this.#scan(tx,
|
|
714
|
+
await this.#scan(tx, canonical, (event) => {
|
|
712
715
|
items.push({
|
|
713
716
|
id: event.id,
|
|
714
717
|
created_at: event.created_at
|
|
715
718
|
});
|
|
716
|
-
return
|
|
719
|
+
return canonical.limit !== void 0 && items.length >= canonical.limit;
|
|
717
720
|
});
|
|
718
721
|
await done;
|
|
719
722
|
items.sort(itemCompare);
|
|
@@ -919,9 +922,10 @@ function inClause(column, count) {
|
|
|
919
922
|
* Compile one NIP-01 filter into query variants. `IN` lists are chunked so a variant never binds
|
|
920
923
|
* more than {@link IN_CHUNK} values per list; variants are the cartesian product of chunks across
|
|
921
924
|
* constrained fields. Multi-letter `#` keys are not NIP-01 tag conditions and are skipped, exactly
|
|
922
|
-
* like `matchFilter`.
|
|
925
|
+
* like `matchFilter`. List fields are never empty — the normalizer rejects them first.
|
|
923
926
|
*/
|
|
924
927
|
function compileFilter(filter) {
|
|
928
|
+
const f = canonicalizeFilter(filter);
|
|
925
929
|
const variants = [];
|
|
926
930
|
const pushIn = (values, clause) => {
|
|
927
931
|
variants.push(chunkValues(values).map((chunk) => ({
|
|
@@ -929,39 +933,21 @@ function compileFilter(filter) {
|
|
|
929
933
|
params: [...chunk]
|
|
930
934
|
})));
|
|
931
935
|
};
|
|
932
|
-
if (
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
if (filter.authors) {
|
|
937
|
-
if (filter.authors.length === 0) return;
|
|
938
|
-
pushIn(filter.authors.map((pk) => pk.toLowerCase()), (n) => inClause("pubkey", n));
|
|
939
|
-
}
|
|
940
|
-
if (filter.kinds) {
|
|
941
|
-
if (filter.kinds.length === 0) return;
|
|
942
|
-
pushIn(filter.kinds, (n) => inClause("kind", n));
|
|
943
|
-
}
|
|
944
|
-
if (filter.since !== void 0) variants.push([{
|
|
936
|
+
if (f.ids) pushIn(f.ids, (n) => inClause("id", n));
|
|
937
|
+
if (f.authors) pushIn(f.authors, (n) => inClause("pubkey", n));
|
|
938
|
+
if (f.kinds) pushIn(f.kinds, (n) => inClause("kind", n));
|
|
939
|
+
if (f.since !== void 0) variants.push([{
|
|
945
940
|
wheres: ["created_at >= ?"],
|
|
946
|
-
params: [
|
|
941
|
+
params: [f.since]
|
|
947
942
|
}]);
|
|
948
|
-
if (
|
|
943
|
+
if (f.until !== void 0) variants.push([{
|
|
949
944
|
wheres: ["created_at <= ?"],
|
|
950
|
-
params: [
|
|
945
|
+
params: [f.until]
|
|
951
946
|
}]);
|
|
952
|
-
for (const
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
const values = filter[key];
|
|
957
|
-
if (!values) continue;
|
|
958
|
-
if (values.length === 0) return;
|
|
959
|
-
const normalized = name === "e" || name === "p" ? values.map((v) => v.toLowerCase()) : values;
|
|
960
|
-
variants.push(chunkValues(normalized).map((chunk) => ({
|
|
961
|
-
wheres: [`EXISTS (SELECT 1 FROM tags t WHERE t.event_id = events.id AND t.name = ? AND ${inClause("t.value", chunk.length)})`],
|
|
962
|
-
params: [name, ...chunk]
|
|
963
|
-
})));
|
|
964
|
-
}
|
|
947
|
+
for (const { name, values } of filterTagConditions(f)) variants.push(chunkValues(values).map((chunk) => ({
|
|
948
|
+
wheres: [`EXISTS (SELECT 1 FROM tags t WHERE t.event_id = events.id AND t.name = ? AND ${inClause("t.value", chunk.length)})`],
|
|
949
|
+
params: [name, ...chunk]
|
|
950
|
+
})));
|
|
965
951
|
const plans = [{
|
|
966
952
|
wheres: [],
|
|
967
953
|
params: []
|
|
@@ -1191,7 +1177,6 @@ var SqliteEventStore = class SqliteEventStore {
|
|
|
1191
1177
|
async #filterRows(filter, select) {
|
|
1192
1178
|
if (filter.limit === 0) return [];
|
|
1193
1179
|
const plans = compileFilter(filter);
|
|
1194
|
-
if (plans === void 0) return [];
|
|
1195
1180
|
if (select === "*") return (await this.#planRows(plans, "*", filter.limit)).map(rowToEvent);
|
|
1196
1181
|
return this.#planRows(plans, "id, created_at", filter.limit);
|
|
1197
1182
|
}
|
|
@@ -1288,4 +1273,4 @@ var SqliteEventStore = class SqliteEventStore {
|
|
|
1288
1273
|
//#endregion
|
|
1289
1274
|
export { IndexedDbEventStore as n, SqliteEventStore as t };
|
|
1290
1275
|
|
|
1291
|
-
//# sourceMappingURL=storage-
|
|
1276
|
+
//# sourceMappingURL=storage-DvDAuxzC.mjs.map
|