@qntx/nostr 0.12.1 → 0.13.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-BrmLOVj_.d.mts → builder-C72nJITG.d.mts} +2 -2
- package/dist/{builder-Cf2N12CG.mjs → builder-CGgM_QHT.mjs} +5 -5
- package/dist/{builder-Cf2N12CG.mjs.map → builder-CGgM_QHT.mjs.map} +1 -1
- package/dist/{client-B3_vToDX.mjs → client-COkEihj_.mjs} +87 -79
- package/dist/client-COkEihj_.mjs.map +1 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -1
- package/dist/core.d.mts +7 -7
- package/dist/core.mjs +9 -9
- package/dist/{emitter-DkCY6NkU.mjs → emitter-u4kJqQDk.mjs} +2 -2
- package/dist/{emitter-DkCY6NkU.mjs.map → emitter-u4kJqQDk.mjs.map} +1 -1
- package/dist/{error-BquXDmIN.d.mts → error-BPqAU5RB.d.mts} +2 -6
- package/dist/{error-BD_0QFKT.d.mts → error-BjSUsx6M.d.mts} +2 -2
- package/dist/{error-D3VNRVhL.mjs → error-Dyb89ahb.mjs} +2 -2
- package/dist/{error-D3VNRVhL.mjs.map → error-Dyb89ahb.mjs.map} +1 -1
- package/dist/{error-DH4guike.mjs → error-QyECUDIp.mjs} +2 -2
- package/dist/{error-DH4guike.mjs.map → error-QyECUDIp.mjs.map} +1 -1
- package/dist/{event-B3ua2_oL.mjs → event-DvM1f03b.mjs} +7 -3
- package/dist/event-DvM1f03b.mjs.map +1 -0
- package/dist/{filter-B16P33dz.mjs → filter-3jOh4uLN.mjs} +50 -20
- package/dist/filter-3jOh4uLN.mjs.map +1 -0
- package/dist/{filter-PG7r79rt.d.mts → filter-oYG2zjJ3.d.mts} +6 -2
- package/dist/{gossip-u0iBlZ1U.d.mts → gossip-BvCItMVZ.d.mts} +3 -3
- package/dist/{gossip-K_JIexQs.mjs → gossip-CRyhK6E6.mjs} +4 -4
- package/dist/{gossip-K_JIexQs.mjs.map → gossip-CRyhK6E6.mjs.map} +1 -1
- package/dist/gossip.d.mts +1 -1
- package/dist/gossip.mjs +1 -1
- package/dist/http-CuazNRIP.mjs.map +1 -1
- package/dist/{index-DD0Ywpay.d.mts → index-B2JlP2Jf.d.mts} +9 -9
- package/dist/{index-yq9xkONo.d.mts → index-DCobP2Jj.d.mts} +10 -10
- package/dist/{index-Hp26bgYG.d.mts → index-Dpj_MUxb.d.mts} +8 -5
- package/dist/{index-rKcmI6ex.d.mts → index-DzHmhHqD.d.mts} +3 -3
- package/dist/{index-SwMknboJ.d.mts → index-YID-zPDF.d.mts} +7 -7
- package/dist/{index-DxQO1eWA.d.mts → index-muydfuYD.d.mts} +21 -21
- package/dist/index.d.mts +19 -20
- package/dist/index.mjs +20 -20
- package/dist/{key-B218_SoL.mjs → key-D_oEutQ6.mjs} +5 -7
- package/dist/{key-B218_SoL.mjs.map → key-D_oEutQ6.mjs.map} +1 -1
- package/dist/{key-CwMVo14x.d.mts → key-DojW2QZl.d.mts} +3 -5
- package/dist/{kind-D8fENiiL.mjs → kind-I2Tvbe82.mjs} +6 -5
- package/dist/kind-I2Tvbe82.mjs.map +1 -0
- package/dist/{loaders-Xdbx-K61.mjs → loaders-BRh2riV9.mjs} +17 -16
- package/dist/loaders-BRh2riV9.mjs.map +1 -0
- package/dist/loaders.d.mts +2 -2
- package/dist/loaders.mjs +1 -1
- package/dist/{memory-VyPnOBU3.mjs → memory-D_GdEQYG.mjs} +2 -2
- package/dist/memory-D_GdEQYG.mjs.map +1 -0
- package/dist/memory-index-3GSrPIWj.mjs +671 -0
- package/dist/memory-index-3GSrPIWj.mjs.map +1 -0
- package/dist/{message-pYfVKxPn.mjs → message-BMfreCQc.mjs} +6 -6
- package/dist/message-BMfreCQc.mjs.map +1 -0
- package/dist/{nip17-BM4hJYBq.d.mts → nip17-BZvFQLcs.d.mts} +4 -4
- package/dist/{nip19-DpPN17bY.d.mts → nip19-D4RKn6GC.d.mts} +2 -2
- package/dist/{nip46-BHaxC9TZ.d.mts → nip46-i8L8_nIm.d.mts} +2 -2
- package/dist/{nip51-D_bu8Hph.d.mts → nip51-C_kTYcud.d.mts} +2 -2
- package/dist/{nip59-CGWfoItR.d.mts → nip59-DfJ6Moeu.d.mts} +2 -2
- package/dist/{nip65-DaWMg943.d.mts → nip65-CPNE0SV0.d.mts} +2 -2
- package/dist/{nip77-wHA_nEV5.d.mts → nip77-DhpoL7d8.d.mts} +2 -2
- package/dist/nips/blossom.d.mts +2 -2
- package/dist/nips/blossom.mjs +3 -3
- package/dist/nips/blossom.mjs.map +1 -1
- package/dist/nips/nip04.d.mts +9 -12
- package/dist/nips/nip04.mjs +38 -45
- package/dist/nips/nip04.mjs.map +1 -1
- package/dist/nips/nip05.d.mts +6 -3
- package/dist/nips/nip05.mjs +6 -3
- package/dist/nips/nip05.mjs.map +1 -1
- package/dist/nips/nip10.d.mts +2 -2
- package/dist/nips/nip10.mjs +4 -4
- package/dist/nips/nip11.d.mts +1 -1
- package/dist/nips/nip11.mjs +1 -1
- package/dist/nips/nip13.d.mts +1 -1
- package/dist/nips/nip13.mjs +1 -1
- package/dist/nips/nip13.mjs.map +1 -1
- package/dist/nips/nip17.d.mts +1 -1
- package/dist/nips/nip17.mjs +4 -4
- package/dist/nips/nip17.mjs.map +1 -1
- package/dist/nips/nip19.d.mts +1 -1
- package/dist/nips/nip19.mjs +2 -2
- package/dist/nips/nip19.mjs.map +1 -1
- package/dist/nips/nip21.d.mts +6 -3
- package/dist/nips/nip21.mjs +8 -5
- package/dist/nips/nip21.mjs.map +1 -1
- package/dist/nips/nip27.d.mts +1 -1
- package/dist/nips/nip27.mjs +15 -9
- package/dist/nips/nip27.mjs.map +1 -1
- package/dist/nips/nip42.mjs +2 -2
- package/dist/nips/nip44.mjs +1 -1
- package/dist/nips/nip46.d.mts +1 -1
- package/dist/nips/nip46.mjs +9 -7
- 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 +5 -5
- package/dist/nips/nip51.mjs.map +1 -1
- package/dist/nips/nip57.mjs +6 -5
- package/dist/nips/nip57.mjs.map +1 -1
- package/dist/nips/nip59.d.mts +1 -1
- package/dist/nips/nip59.mjs +5 -5
- package/dist/nips/nip59.mjs.map +1 -1
- package/dist/nips/nip65.d.mts +1 -1
- package/dist/nips/nip65.mjs +3 -3
- package/dist/nips/nip77.d.mts +1 -1
- package/dist/nips/nip77.mjs +1 -1
- package/dist/nips/nip77.mjs.map +1 -1
- package/dist/nips/nip98.d.mts +1 -1
- package/dist/nips/nip98.mjs +9 -6
- package/dist/nips/nip98.mjs.map +1 -1
- package/dist/{pool-DUuYJ0x4.d.mts → pool-MehgIIzm.d.mts} +20 -16
- package/dist/{pool-THuuOW4S.mjs → pool-lMT_mvzn.mjs} +25 -25
- package/dist/pool-lMT_mvzn.mjs.map +1 -0
- package/dist/{put-DRmV1wHD.mjs → put-BEM5j8xK.mjs} +5 -5
- package/dist/{put-DRmV1wHD.mjs.map → put-BEM5j8xK.mjs.map} +1 -1
- package/dist/{reactive-jyKrA9ej.d.mts → reactive-B7lPFgTS.d.mts} +3 -3
- package/dist/{reactive--pDpLt2H.mjs → reactive-DFHY-f00.mjs} +24 -9
- package/dist/reactive-DFHY-f00.mjs.map +1 -0
- package/dist/{relay-BGPqiGKF.mjs → relay-DIR77xQ2.mjs} +102 -89
- package/dist/relay-DIR77xQ2.mjs.map +1 -0
- package/dist/relay.d.mts +5 -5
- package/dist/relay.mjs +3 -3
- package/dist/{report-CHld4uWj.mjs → report-D2BM5dgn.mjs} +2 -2
- package/dist/{report-CHld4uWj.mjs.map → report-D2BM5dgn.mjs.map} +1 -1
- package/dist/{signer-CNTIeYH4.mjs → signer-DJcmC4nA.mjs} +17 -17
- package/dist/signer-DJcmC4nA.mjs.map +1 -0
- package/dist/signer.d.mts +1 -1
- package/dist/signer.mjs +2 -2
- package/dist/{storage-B5Wdt_Mn.mjs → storage-n2BE1bdY.mjs} +108 -179
- package/dist/storage-n2BE1bdY.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 -1
- package/dist/testing.d.mts +4 -4
- package/dist/testing.mjs +35 -10
- package/dist/testing.mjs.map +1 -1
- package/dist/{types-CqhnT-Mx.d.mts → types-DiVVyf1e.d.mts} +2 -2
- package/dist/{util-DDpPkiED.mjs → util-DfsyJ9nz.mjs} +30 -10
- package/dist/util-DfsyJ9nz.mjs.map +1 -0
- package/dist/{verifier-CjH1vnP2.mjs → verifier-DEHUy4AD.mjs} +3 -3
- package/dist/{verifier-CjH1vnP2.mjs.map → verifier-DEHUy4AD.mjs.map} +1 -1
- package/dist/{verifier-Cl24c9ph.d.mts → verifier-DhAbqVJF.d.mts} +2 -2
- package/dist/{websocket-CmeqvAkp.d.mts → websocket-DaCvJkF9.d.mts} +2 -5
- package/package.json +4 -3
- package/dist/client-B3_vToDX.mjs.map +0 -1
- package/dist/event-B3ua2_oL.mjs.map +0 -1
- package/dist/filter-B16P33dz.mjs.map +0 -1
- package/dist/kind-D8fENiiL.mjs.map +0 -1
- package/dist/loaders-Xdbx-K61.mjs.map +0 -1
- package/dist/memory-VyPnOBU3.mjs.map +0 -1
- package/dist/memory-index-Bhwe0ZdG.mjs +0 -374
- package/dist/memory-index-Bhwe0ZdG.mjs.map +0 -1
- package/dist/message-pYfVKxPn.mjs.map +0 -1
- package/dist/pool-THuuOW4S.mjs.map +0 -1
- package/dist/reactive--pDpLt2H.mjs.map +0 -1
- package/dist/relay-BGPqiGKF.mjs.map +0 -1
- package/dist/signer-CNTIeYH4.mjs.map +0 -1
- package/dist/storage-B5Wdt_Mn.mjs.map +0 -1
- package/dist/util-DDpPkiED.mjs.map +0 -1
package/dist/storage.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as PutResult, n as NegentropyItem, r as OutboxBound, t as EventStore } from "./types-
|
|
2
|
-
import { t as StorageError } from "./error-
|
|
3
|
-
import { a as IndexedDbEventStoreOptions, c as MemoryIndexOptions, i as IndexedDbEventStore, n as SqlValue, o as MemoryEventStore, r as SqliteEventStore, s as MemoryIndex, t as SqlDriver } from "./index-
|
|
1
|
+
import { i as PutResult, n as NegentropyItem, r as OutboxBound, t as EventStore } from "./types-DiVVyf1e.mjs";
|
|
2
|
+
import { t as StorageError } from "./error-BjSUsx6M.mjs";
|
|
3
|
+
import { a as IndexedDbEventStoreOptions, c as MemoryIndexOptions, i as IndexedDbEventStore, n as SqlValue, o as MemoryEventStore, r as SqliteEventStore, s as MemoryIndex, t as SqlDriver } from "./index-DzHmhHqD.mjs";
|
|
4
4
|
export { type EventStore, IndexedDbEventStore, type IndexedDbEventStoreOptions, MemoryEventStore, MemoryIndex, type MemoryIndexOptions, type NegentropyItem, type OutboxBound, type PutResult, type SqlDriver, type SqlValue, SqliteEventStore, StorageError };
|
package/dist/storage.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as StorageError } from "./error-
|
|
2
|
-
import { t as MemoryIndex } from "./memory-index-
|
|
3
|
-
import { t as MemoryEventStore } from "./memory-
|
|
4
|
-
import { n as IndexedDbEventStore, t as SqliteEventStore } from "./storage-
|
|
1
|
+
import { t as StorageError } from "./error-QyECUDIp.mjs";
|
|
2
|
+
import { t as MemoryIndex } from "./memory-index-3GSrPIWj.mjs";
|
|
3
|
+
import { t as MemoryEventStore } from "./memory-D_GdEQYG.mjs";
|
|
4
|
+
import { n as IndexedDbEventStore, t as SqliteEventStore } from "./storage-n2BE1bdY.mjs";
|
|
5
5
|
export { IndexedDbEventStore, MemoryEventStore, MemoryIndex, SqliteEventStore, StorageError };
|
package/dist/store.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as Watch, n as ReactiveEventStoreEventMap, r as ReactiveEventStoreOptions, t as ReactiveEventStore } from "./reactive-
|
|
1
|
+
import { i as Watch, n as ReactiveEventStoreEventMap, r as ReactiveEventStoreOptions, t as ReactiveEventStore } from "./reactive-B7lPFgTS.mjs";
|
|
2
2
|
export { ReactiveEventStore, type ReactiveEventStoreEventMap, type ReactiveEventStoreOptions, type Watch };
|
package/dist/store.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as ReactiveEventStore } from "./reactive
|
|
1
|
+
import { t as ReactiveEventStore } from "./reactive-DFHY-f00.mjs";
|
|
2
2
|
export { ReactiveEventStore };
|
package/dist/testing.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as Event } from "./event-j3BwI5dF.mjs";
|
|
2
|
-
import { t as WebSocketConstructor } from "./websocket-
|
|
3
|
-
import { t as EventStore } from "./types-
|
|
4
|
-
import { a as Nip46Response } from "./nip46-
|
|
2
|
+
import { t as WebSocketConstructor } from "./websocket-DaCvJkF9.mjs";
|
|
3
|
+
import { t as EventStore } from "./types-DiVVyf1e.mjs";
|
|
4
|
+
import { a as Nip46Response } from "./nip46-i8L8_nIm.mjs";
|
|
5
5
|
//#region src/testing/relay-core.d.ts
|
|
6
6
|
type FakeRelayOptions = {
|
|
7
7
|
/** Delay before every relay→client message. */
|
|
@@ -49,7 +49,7 @@ type FakeRelay = {
|
|
|
49
49
|
//#endregion
|
|
50
50
|
//#region src/testing/network.d.ts
|
|
51
51
|
type FakeRelayNetwork = {
|
|
52
|
-
/** Pass to `Client` / `Pool` / `
|
|
52
|
+
/** Pass to `Client` / `Pool` / `Relay` as `websocketImplementation`. */
|
|
53
53
|
readonly websocketImplementation: WebSocketConstructor;
|
|
54
54
|
/** Get-or-create a relay handle by normalized URL. */
|
|
55
55
|
relay: (url: string, opts?: FakeRelayOptions) => FakeRelay;
|
package/dist/testing.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as Kind } from "./kind-
|
|
3
|
-
import { h as validateSignedEvent, t as compareEventsDesc, x as isTag } from "./event-
|
|
4
|
-
import { r as finalizeEvent, t as Keys } from "./key-
|
|
5
|
-
import { n as verifyEvent } from "./verifier-
|
|
6
|
-
import { t as EventBuilder } from "./builder-
|
|
7
|
-
import {
|
|
1
|
+
import { C as errorMessage, f as normalizeURL, i as bytesToHex, p as nowSeconds, u as isRecord } from "./util-DfsyJ9nz.mjs";
|
|
2
|
+
import { t as Kind } from "./kind-I2Tvbe82.mjs";
|
|
3
|
+
import { h as validateSignedEvent, t as compareEventsDesc, x as isTag } from "./event-DvM1f03b.mjs";
|
|
4
|
+
import { r as finalizeEvent, t as Keys } from "./key-D_oEutQ6.mjs";
|
|
5
|
+
import { n as verifyEvent } from "./verifier-DEHUy4AD.mjs";
|
|
6
|
+
import { t as EventBuilder } from "./builder-CGgM_QHT.mjs";
|
|
7
|
+
import { o as matchFilter } from "./filter-3jOh4uLN.mjs";
|
|
8
8
|
import { decrypt, encrypt, getConversationKey } from "./nips/nip44.mjs";
|
|
9
9
|
import { decodeNip46Request, encodeNip46Response } from "./nips/nip46.mjs";
|
|
10
10
|
import { Negentropy, storageFromEvents } from "./nips/nip77.mjs";
|
|
11
|
-
import { t as MemoryEventStore } from "./memory-
|
|
11
|
+
import { t as MemoryEventStore } from "./memory-D_GdEQYG.mjs";
|
|
12
12
|
//#region src/testing/relay-core.ts
|
|
13
13
|
/** Wire filters are trusted as NIP-01 filters once they are records. */
|
|
14
14
|
function isFilter(value) {
|
|
@@ -111,7 +111,8 @@ var FakeRelayCore = class {
|
|
|
111
111
|
eosed: /* @__PURE__ */ new Set(),
|
|
112
112
|
negs: /* @__PURE__ */ new Map(),
|
|
113
113
|
authed: false,
|
|
114
|
-
queue: Promise.resolve()
|
|
114
|
+
queue: Promise.resolve(),
|
|
115
|
+
timers: /* @__PURE__ */ new Set()
|
|
115
116
|
};
|
|
116
117
|
this.#sessions.add(session);
|
|
117
118
|
if (this.#opts.auth) this.#send(session, ["AUTH", this.#opts.auth.challenge]);
|
|
@@ -119,6 +120,8 @@ var FakeRelayCore = class {
|
|
|
119
120
|
}
|
|
120
121
|
detach(session) {
|
|
121
122
|
this.#sessions.delete(session);
|
|
123
|
+
for (const timer of session.timers) clearTimeout(timer);
|
|
124
|
+
session.timers.clear();
|
|
122
125
|
}
|
|
123
126
|
/** Feed a raw client→relay frame. Messages of one session are handled in order. */
|
|
124
127
|
handleMessage(session, raw) {
|
|
@@ -142,7 +145,12 @@ var FakeRelayCore = class {
|
|
|
142
145
|
const payload = JSON.stringify(message);
|
|
143
146
|
const { latencyMs } = this.#opts;
|
|
144
147
|
if (latencyMs !== void 0 && latencyMs > 0) {
|
|
145
|
-
setTimeout(() =>
|
|
148
|
+
const timer = setTimeout(() => {
|
|
149
|
+
session.timers.delete(timer);
|
|
150
|
+
if (!this.#sessions.has(session)) return;
|
|
151
|
+
session.transport.send(payload);
|
|
152
|
+
}, latencyMs);
|
|
153
|
+
session.timers.add(timer);
|
|
146
154
|
return;
|
|
147
155
|
}
|
|
148
156
|
session.transport.send(payload);
|
|
@@ -488,6 +496,7 @@ function createFakeRelayNetwork(defaults = {}) {
|
|
|
488
496
|
for (const data of queued) this.#core.handleMessage(this.#session, data);
|
|
489
497
|
}
|
|
490
498
|
send(data) {
|
|
499
|
+
if (this.readyState === FakeSocket.CONNECTING) throw new Error("InvalidStateError: WebSocket is not open: readyState 0 (CONNECTING)");
|
|
491
500
|
if (this.readyState !== FakeSocket.OPEN) return;
|
|
492
501
|
this.sent.push(data);
|
|
493
502
|
if (this.#session) this.#core.handleMessage(this.#session, data);
|
|
@@ -747,6 +756,22 @@ const eventStoreConformanceCases = [
|
|
|
747
756
|
eq(ids(await store.query([{ ids: [target.id, tagged.id] }])).sort(), [tagged.id, target.id].sort(), "ids filter");
|
|
748
757
|
}
|
|
749
758
|
},
|
|
759
|
+
{
|
|
760
|
+
name: "#e/#p tag values compare verbatim, lowercasing only the filter side",
|
|
761
|
+
async run(store) {
|
|
762
|
+
const target = note(bob(), "target", 1);
|
|
763
|
+
const upper = note(alice(), "upper", 2, [["e", target.id.toUpperCase()], ["p", bob().publicKey.toUpperCase()]]);
|
|
764
|
+
const lower = note(alice(), "lower", 3, [["e", target.id], ["p", bob().publicKey]]);
|
|
765
|
+
await store.putMany([
|
|
766
|
+
target,
|
|
767
|
+
upper,
|
|
768
|
+
lower
|
|
769
|
+
]);
|
|
770
|
+
eq(ids(await store.query([{ "#e": [target.id] }])), [lower.id], "#e lowercase filter");
|
|
771
|
+
eq(ids(await store.query([{ "#e": [target.id.toUpperCase()] }])), [lower.id], "#e uppercase filter");
|
|
772
|
+
eq(ids(await store.query([{ "#p": [bob().publicKey.toUpperCase()] }])), [lower.id], "#p uppercase filter");
|
|
773
|
+
}
|
|
774
|
+
},
|
|
750
775
|
{
|
|
751
776
|
name: "multi-letter # keys are ignored (NIP-01 single-letter tag conditions only)",
|
|
752
777
|
async run(store) {
|
package/dist/testing.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.mjs","names":["parts","nip44Decrypt","nip44Encrypt"],"sources":["../src/testing/relay-core.ts","../src/testing/network.ts","../src/testing/serve.ts","../src/testing/conformance.ts","../src/testing/nip46.ts"],"sourcesContent":["import { errorMessage } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { compareEventsDesc, validateSignedEvent } from \"../core/event.ts\";\nimport { matchFilter } from \"../core/filter.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { bytesToHex, isRecord, normalizeURL } from \"../core/util.ts\";\nimport { verifyEvent } from \"../core/verifier.ts\";\nimport { Negentropy, PROTOCOL_VERSION, storageFromEvents } from \"../nips/nip77.ts\";\nimport { MemoryEventStore } from \"../storage/memory.ts\";\nimport type { PutResult } from \"../storage/types.ts\";\n\nexport type FakeRelayOptions = {\n /** Delay before every relay→client message. */\n latencyMs?: number | undefined;\n auth?:\n | {\n /** Sent as `[\"AUTH\", challenge]` right after the socket opens. */\n challenge: string;\n /** REQ whose filters can match these kinds is CLOSED `auth-required:` until AUTH succeeds. */\n readKinds?: ReadonlyArray<number> | undefined;\n /** EVENT → `[\"OK\", id, false, \"auth-required: ...\"]` until AUTH succeeds. */\n writes?: boolean | undefined;\n }\n | undefined;\n /** EVENT → `[\"OK\", id, false, \"rate-limited: ...\"]`; nothing is stored. */\n rateLimited?: boolean | undefined;\n /** Out-of-order reply: send EOSE first, then the stored events. */\n eoseBeforeEvents?: boolean | undefined;\n};\n\nexport type FakeRelay = {\n readonly url: string;\n seed: (events: ReadonlyArray<Event>) => void;\n events: () => ReadonlyArray<Event>;\n /**\n * Store an event and broadcast it to matching subscriptions. Unlike client EVENT writes the relay\n * does not verify it here, so forged events (mismatched id/signature) are delivered as-is.\n */\n inject: (event: Event) => void;\n /**\n * Push an event to matching subscriptions without storing it: a resubscription never re-serves\n * it. Use it for forged events (mismatched id/signature) when exercising client-side verification\n * failure handling.\n */\n deliver: (event: Event) => void;\n /**\n * Live subscriptions whose filters match the event and whose initial dump already finished (their\n * EOSE is on the wire). A publish that waits for this to be non-zero always lands on the client's\n * live path — an earlier event is just another initial-page row.\n */\n matchingLiveSubscribers: (event: Event) => number;\n disconnect: () => void;\n closeSubscriptions: (reason: string) => void;\n configure: (opts: Partial<FakeRelayOptions>) => void;\n /** Every parsed client→relay message received, in arrival order. */\n clientMessages: () => ReadonlyArray<unknown>;\n};\n\n/** One relay→client transport (an in-process socket or a `ws` connection). */\nexport type RelayTransport = {\n send: (data: string) => void;\n close: () => void;\n};\n\nexport type FakeRelaySession = {\n transport: RelayTransport;\n subs: Map<string, Filter[]>;\n /** REQ ids whose initial dump finished — EOSE already sent to the client. */\n eosed: Set<string>;\n negs: Map<string, Negentropy>;\n authed: boolean;\n queue: Promise<void>;\n};\n\n/** Wire filters are trusted as NIP-01 filters once they are records. */\nfunction isFilter(value: unknown): value is Filter {\n return isRecord(value);\n}\n\nfunction searchMatch(filter: Filter, event: Event): boolean {\n if (filter.search === undefined) {\n return true;\n }\n return event.content.toLowerCase().includes(filter.search.toLowerCase());\n}\n\nfunction subMatches(filters: ReadonlyArray<Filter>, event: Event): boolean {\n return filters.some((f) => matchFilter(f, event) && searchMatch(f, event));\n}\n\n/** Put outcomes that made the event part of the relay's live stream. */\nfunction isLivePut(result: PutResult): boolean {\n return (\n result === \"accepted\" || result === \"replaced\" || result === \"deleted\" || result === \"ephemeral\"\n );\n}\n\nfunction mayReadKinds(filters: ReadonlyArray<Filter>, readKinds: ReadonlyArray<number>): boolean {\n return filters.some((f) => f.kinds === undefined || f.kinds.some((k) => readKinds.includes(k)));\n}\n\n/**\n * Transport-agnostic fake relay: one NIP-01/42/45/50/77 session state machine per connection,\n * storage via {@link MemoryEventStore}. No timers except `latencyMs`; no global state.\n */\nexport class FakeRelayCore implements FakeRelay {\n readonly url: string;\n #opts: FakeRelayOptions;\n readonly #store = new MemoryEventStore();\n readonly #sessions = new Set<FakeRelaySession>();\n #mirror = new Map<string, Event>();\n readonly #inbox: unknown[] = [];\n #pending: Promise<void> = Promise.resolve();\n\n constructor(url: string, opts: FakeRelayOptions = {}) {\n this.url = normalizeURL(url);\n this.#opts = opts;\n }\n\n configure(opts: Partial<FakeRelayOptions>): void {\n this.#opts = { ...this.#opts, ...opts };\n }\n\n seed(events: ReadonlyArray<Event>): void {\n for (const event of events) {\n this.#mirror.set(event.id, event);\n }\n this.#pending = (async (): Promise<void> => {\n await this.#pending;\n for (const event of events) {\n // Sequential puts keep per-event store ordering for replaceables.\n // oxlint-disable-next-line eslint/no-await-in-loop\n await this.#store.put(event);\n }\n await this.#refreshMirror();\n })();\n }\n\n events(): ReadonlyArray<Event> {\n return [...this.#mirror.values()];\n }\n\n inject(event: Event): void {\n this.#pending = (async (): Promise<void> => {\n await this.#pending;\n const result = await this.#store.put(event);\n await this.#refreshMirror();\n if (isLivePut(result)) {\n this.#deliver(event);\n }\n })();\n }\n\n deliver(event: Event): void {\n this.#pending = (async (): Promise<void> => {\n await this.#pending;\n this.#deliver(event);\n })();\n }\n\n matchingLiveSubscribers(event: Event): number {\n let count = 0;\n for (const session of this.#sessions) {\n for (const id of session.eosed) {\n const filters = session.subs.get(id);\n if (filters !== undefined && subMatches(filters, event)) {\n count += 1;\n }\n }\n }\n return count;\n }\n\n disconnect(): void {\n const sessions: FakeRelaySession[] = [...this.#sessions];\n this.#sessions.clear();\n for (const session of sessions) {\n session.transport.close();\n }\n }\n\n closeSubscriptions(reason: string): void {\n for (const session of this.#sessions) {\n for (const id of session.subs.keys()) {\n this.#send(session, [\"CLOSED\", id, reason]);\n }\n session.subs.clear();\n session.eosed.clear();\n session.negs.clear();\n }\n }\n\n clientMessages(): ReadonlyArray<unknown> {\n return this.#inbox;\n }\n\n /** Attach a transport; returns a handle for {@link handleMessage}/detach. */\n connect(transport: RelayTransport): FakeRelaySession {\n const session: FakeRelaySession = {\n transport,\n subs: new Map(),\n eosed: new Set(),\n negs: new Map(),\n authed: false,\n queue: Promise.resolve(),\n };\n this.#sessions.add(session);\n if (this.#opts.auth) {\n this.#send(session, [\"AUTH\", this.#opts.auth.challenge]);\n }\n return session;\n }\n\n detach(session: FakeRelaySession): void {\n this.#sessions.delete(session);\n }\n\n /** Feed a raw client→relay frame. Messages of one session are handled in order. */\n handleMessage(session: FakeRelaySession, raw: string): void {\n // A handler throw must not poison the session queue: NOTICE and continue.\n session.queue = (async (): Promise<void> => {\n try {\n await session.queue;\n await this.#handle(session, raw);\n } catch (error) {\n const message = errorMessage(error);\n try {\n this.#send(session, [\"NOTICE\", `error: ${message}`]);\n } catch {\n // transport gone\n }\n }\n })();\n }\n\n async #refreshMirror(): Promise<void> {\n const all = await this.#store.query([{}]);\n this.#mirror = new Map(all.map((e) => [e.id, e]));\n }\n\n #send(session: FakeRelaySession, message: unknown[]): void {\n const payload = JSON.stringify(message);\n const { latencyMs } = this.#opts;\n if (latencyMs !== undefined && latencyMs > 0) {\n setTimeout(() => session.transport.send(payload), latencyMs);\n return;\n }\n session.transport.send(payload);\n }\n\n #deliver(event: Event): void {\n for (const session of this.#sessions) {\n for (const [id, filters] of session.subs) {\n if (subMatches(filters, event)) {\n this.#send(session, [\"EVENT\", id, event]);\n }\n }\n }\n }\n\n async #matched(filters: Filter[]): Promise<Event[]> {\n // Per-filter NIP-01 limit: query unbounded, apply search, keep that\n // filter's newest — never a cross-filter minimum.\n const keptPerFilter = await Promise.all(\n filters.map(async (filter): Promise<Event[]> => {\n const unbounded = { ...filter };\n delete unbounded.limit;\n const queried = await this.#store.query([unbounded]);\n const rows = queried.filter((event) => searchMatch(filter, event));\n rows.sort(compareEventsDesc);\n return filter.limit === undefined ? rows : rows.slice(0, filter.limit);\n }),\n );\n const seen = new Map<string, Event>();\n for (const kept of keptPerFilter) {\n for (const event of kept) {\n if (!seen.has(event.id)) {\n seen.set(event.id, event);\n }\n }\n }\n const matched = [...seen.values()];\n matched.sort(compareEventsDesc);\n return matched;\n }\n\n async #handle(session: FakeRelaySession, raw: string): Promise<void> {\n await this.#pending;\n let msg: unknown;\n try {\n msg = JSON.parse(raw);\n } catch {\n this.#send(session, [\"NOTICE\", \"error: invalid message\"]);\n return;\n }\n if (!Array.isArray(msg)) {\n this.#send(session, [\"NOTICE\", \"error: invalid message\"]);\n return;\n }\n const items: unknown[] = msg;\n const [type] = items;\n if (typeof type !== \"string\") {\n this.#send(session, [\"NOTICE\", \"error: invalid message\"]);\n return;\n }\n this.#inbox.push(msg);\n\n switch (type) {\n case \"EVENT\": {\n const [, rawEvent] = items;\n if (!isRecord(rawEvent) || typeof rawEvent[\"id\"] !== \"string\") {\n this.#send(session, [\"NOTICE\", \"invalid: malformed event\"]);\n return;\n }\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- wire record trusted as Event; verifyEvent below is the authoritative check\n const event = rawEvent as Event;\n if (this.#opts.rateLimited === true) {\n this.#send(session, [\"OK\", event.id, false, \"rate-limited: slow down\"]);\n return;\n }\n if (this.#opts.auth?.writes === true && !session.authed) {\n this.#send(session, [\"OK\", event.id, false, \"auth-required: login first\"]);\n return;\n }\n if (!verifyEvent(event)) {\n this.#send(session, [\"OK\", event.id, false, \"invalid: signature or id\"]);\n return;\n }\n const result = await this.#store.put(event);\n await this.#refreshMirror();\n if (result === \"rejected\") {\n this.#send(session, [\n \"OK\",\n event.id,\n false,\n \"invalid: a newer version of this event exists\",\n ]);\n return;\n }\n if (result === \"invalid\") {\n this.#send(session, [\"OK\", event.id, false, \"invalid: malformed event\"]);\n return;\n }\n if (result === \"duplicate\") {\n this.#send(session, [\"OK\", event.id, true, \"duplicate: already have this event\"]);\n return;\n }\n this.#send(session, [\"OK\", event.id, true, \"\"]);\n this.#deliver(event);\n return;\n }\n case \"REQ\": {\n const [, id] = items;\n const filters = items.slice(2);\n if (typeof id !== \"string\" || filters.length === 0 || !filters.every(isFilter)) {\n this.#send(session, [\"NOTICE\", \"error: invalid REQ\"]);\n return;\n }\n const readKinds = this.#opts.auth?.readKinds;\n if (readKinds !== undefined && !session.authed && mayReadKinds(filters, readKinds)) {\n this.#send(session, [\"CLOSED\", id, \"auth-required: we only serve to authed users\"]);\n session.subs.delete(id);\n session.eosed.delete(id);\n return;\n }\n session.subs.set(id, filters);\n session.eosed.delete(id);\n const matched = await this.#matched(filters);\n if (this.#opts.eoseBeforeEvents === true) {\n this.#send(session, [\"EOSE\", id]);\n session.eosed.add(id);\n }\n for (const event of matched) {\n this.#send(session, [\"EVENT\", id, event]);\n }\n if (this.#opts.eoseBeforeEvents !== true) {\n this.#send(session, [\"EOSE\", id]);\n session.eosed.add(id);\n }\n return;\n }\n case \"CLOSE\": {\n const [, id] = items;\n if (typeof id === \"string\") {\n session.subs.delete(id);\n session.eosed.delete(id);\n }\n return;\n }\n case \"COUNT\": {\n const [, id] = items;\n const filters = items.slice(2);\n if (typeof id !== \"string\" || !filters.every(isFilter)) {\n this.#send(session, [\"NOTICE\", \"error: invalid COUNT\"]);\n return;\n }\n const matched = await this.#matched(filters);\n this.#send(session, [\"COUNT\", id, { count: matched.length }]);\n return;\n }\n case \"AUTH\": {\n const { auth } = this.#opts;\n const [, rawAuth] = items;\n if (!validateSignedEvent(rawAuth)) {\n this.#send(session, [\"OK\", \"0\".repeat(64), false, \"error: invalid auth event\"]);\n return;\n }\n const challengeTag = rawAuth.tags.find((t) => t[0] === \"challenge\")?.[1];\n const relayTag = rawAuth.tags.find((t) => t[0] === \"relay\")?.[1];\n let relayOk = false;\n if (relayTag !== undefined) {\n try {\n relayOk = normalizeURL(relayTag) === this.url;\n } catch {\n // unparseable relay tag\n }\n }\n if (\n auth !== undefined &&\n rawAuth.kind === Kind.ClientAuth &&\n verifyEvent(rawAuth) &&\n challengeTag === auth.challenge &&\n relayOk\n ) {\n session.authed = true;\n this.#send(session, [\"OK\", rawAuth.id, true, \"\"]);\n return;\n }\n this.#send(session, [\"OK\", rawAuth.id, false, \"error: invalid auth event\"]);\n return;\n }\n case \"NEG-OPEN\": {\n if (items.length === 5) {\n this.#send(session, [\"NEG-ERR\", items[1], \"error: obsolete 5-element NEG-OPEN\"]);\n return;\n }\n const [, id, rawFilter, negMessage] = items;\n if (\n items.length !== 4 ||\n typeof id !== \"string\" ||\n !isFilter(rawFilter) ||\n typeof negMessage !== \"string\"\n ) {\n const badId = typeof id === \"string\" ? id : \"\";\n this.#send(session, [\"NEG-ERR\", badId, \"error: invalid NEG-OPEN\"]);\n return;\n }\n // The NIP-77 filter is a plain NIP-01 filter — `limit` applies.\n const matched = await this.#matched([rawFilter]);\n const neg = new Negentropy(storageFromEvents(matched));\n let opened: string | undefined;\n try {\n opened = neg.reconcile(negMessage).nextMessage ?? undefined;\n } catch {\n this.#send(session, [\"NEG-ERR\", id, \"error: invalid negentropy message\"]);\n return;\n }\n session.negs.set(id, neg);\n this.#send(session, [\n \"NEG-MSG\",\n id,\n opened ?? bytesToHex(new Uint8Array([PROTOCOL_VERSION])),\n ]);\n return;\n }\n case \"NEG-MSG\": {\n const [, id, negMessage] = items;\n if (items.length !== 3 || typeof id !== \"string\" || typeof negMessage !== \"string\") {\n return;\n }\n const handle = session.negs.get(id);\n if (handle === undefined) {\n this.#send(session, [\"NEG-ERR\", id, \"closed: unknown subscription\"]);\n return;\n }\n let next: string | undefined;\n try {\n next = handle.reconcile(negMessage).nextMessage ?? undefined;\n } catch {\n session.negs.delete(id);\n this.#send(session, [\"NEG-ERR\", id, \"error: invalid negentropy message\"]);\n return;\n }\n this.#send(session, [\n \"NEG-MSG\",\n id,\n next ?? bytesToHex(new Uint8Array([PROTOCOL_VERSION])),\n ]);\n return;\n }\n case \"NEG-CLOSE\": {\n const [, id] = items;\n if (typeof id === \"string\") {\n session.negs.delete(id);\n }\n return;\n }\n default:\n this.#send(session, [\"NOTICE\", `error: unsupported ${type}`]);\n }\n }\n}\n","import { normalizeURL } from \"../core/util.ts\";\nimport type { WebSocketConstructor } from \"../relay/websocket.ts\";\nimport { FakeRelayCore } from \"./relay-core.ts\";\nimport type { FakeRelay, FakeRelayOptions, RelayTransport } from \"./relay-core.ts\";\n\nexport { type FakeRelay, type FakeRelayOptions } from \"./relay-core.ts\";\nexport type FakeRelayNetwork = {\n /** Pass to `Client` / `Pool` / `useWebSocketImplementation`. */\n readonly websocketImplementation: WebSocketConstructor;\n /** Get-or-create a relay handle by normalized URL. */\n relay: (url: string, opts?: FakeRelayOptions) => FakeRelay;\n close: () => void;\n};\n\ntype Listener = (ev: unknown) => void;\n\n/**\n * In-process fake relay network. `new websocketImplementation(url)` opens a socket bound to the\n * network's relay for that URL; `send` is dispatched to the relay session directly (no polling).\n */\nexport function createFakeRelayNetwork(defaults: FakeRelayOptions = {}): FakeRelayNetwork {\n const cores = new Map<string, FakeRelayCore>();\n\n const coreFor = (url: string, opts?: FakeRelayOptions): FakeRelayCore => {\n const key = normalizeURL(url);\n let core = cores.get(key);\n if (!core) {\n core = new FakeRelayCore(key, { ...defaults, ...opts });\n cores.set(key, core);\n } else if (opts) {\n core.configure(opts);\n }\n return core;\n };\n\n class FakeSocket {\n static readonly OPEN = 1;\n static readonly CONNECTING = 0;\n static readonly CLOSING = 2;\n static readonly CLOSED = 3;\n\n readonly url: string;\n /** Client→relay frames, for tests that inspect wire traffic. */\n readonly sent: string[] = [];\n readyState = FakeSocket.CONNECTING;\n readonly #listeners = new Map<string, Set<Listener>>();\n #session: ReturnType<FakeRelayCore[\"connect\"]> | undefined;\n #queuedSends: string[] = [];\n readonly #core: FakeRelayCore;\n\n constructor(url: string) {\n this.url = url;\n this.#core = coreFor(url);\n queueMicrotask(() => this.#open());\n }\n\n #open(): void {\n if (this.readyState !== FakeSocket.CONNECTING) {\n return;\n }\n this.readyState = FakeSocket.OPEN;\n const transport: RelayTransport = {\n send: (data) => this.#emit(\"message\", { data }),\n close: () => this.#terminate(),\n };\n // \"open\" listeners may send immediately; relay frames (e.g. the AUTH\n // challenge) must follow \"open\", so connect afterwards and flush.\n this.#emit(\"open\", {});\n this.#session = this.#core.connect(transport);\n const queued = this.#queuedSends;\n this.#queuedSends = [];\n for (const data of queued) {\n this.#core.handleMessage(this.#session, data);\n }\n }\n\n send(data: string): void {\n if (this.readyState !== FakeSocket.OPEN) {\n return;\n }\n this.sent.push(data);\n if (this.#session) {\n this.#core.handleMessage(this.#session, data);\n } else {\n this.#queuedSends.push(data);\n }\n }\n\n close(): void {\n this.#terminate();\n }\n\n #terminate(): void {\n if (this.readyState === FakeSocket.CLOSING || this.readyState === FakeSocket.CLOSED) {\n this.readyState = FakeSocket.CLOSED;\n return;\n }\n this.readyState = FakeSocket.CLOSED;\n const session = this.#session;\n this.#session = undefined;\n if (session) {\n this.#core.detach(session);\n }\n this.#emit(\"close\", {});\n }\n\n addEventListener(type: string, listener: Listener): void {\n let set = this.#listeners.get(type);\n if (!set) {\n set = new Set();\n this.#listeners.set(type, set);\n }\n set.add(listener);\n }\n\n removeEventListener(type: string, listener: Listener): void {\n this.#listeners.get(type)?.delete(listener);\n }\n\n #emit(type: string, ev: unknown): void {\n for (const listener of this.#listeners.get(type) ?? []) {\n listener(ev);\n }\n }\n }\n\n return {\n websocketImplementation: FakeSocket,\n relay: coreFor,\n close() {\n for (const core of cores.values()) {\n core.disconnect();\n }\n cores.clear();\n },\n };\n}\n","import { FakeRelayCore } from \"./relay-core.ts\";\nimport type { FakeRelay, FakeRelayOptions, RelayTransport } from \"./relay-core.ts\";\n\nexport type ServedFakeRelay = {\n url: string;\n relay: FakeRelay;\n close: () => Promise<void>;\n};\n\n/**\n * Serve the fake relay on a real WebSocket port via the optional `ws` peer dependency (dynamic\n * import — `@qntx/nostr/testing` never requires `ws` unless you serve). `port: 0` picks an\n * ephemeral port.\n */\nexport async function serveFakeRelay(\n opts: FakeRelayOptions & { port?: number; host?: string } = {},\n): Promise<ServedFakeRelay> {\n const { port = 0, host = \"127.0.0.1\", ...relayOpts } = opts;\n const { WebSocketServer } = await import(\"ws\");\n const wss = new WebSocketServer({ port, host });\n await new Promise<void>((resolve, reject) => {\n wss.once(\"listening\", () => resolve());\n wss.once(\"error\", (err) => reject(err));\n });\n const address = wss.address();\n const bound = typeof address === \"object\" && address !== null ? address.port : port;\n const url = `ws://${host}:${bound}`;\n const core = new FakeRelayCore(url, relayOpts);\n\n wss.on(\"connection\", (socket) => {\n const transport: RelayTransport = {\n send: (data) => socket.send(data),\n close: () => socket.close(),\n };\n const session = core.connect(transport);\n socket.on(\"message\", (data: unknown) => {\n core.handleMessage(session, String(data));\n });\n socket.on(\"close\", () => core.detach(session));\n socket.on(\"error\", () => core.detach(session));\n });\n\n return {\n url,\n relay: core,\n close: async () =>\n new Promise((resolve) => {\n core.disconnect();\n for (const client of wss.clients) {\n client.terminate();\n }\n wss.close(() => resolve());\n }),\n };\n}\n","import { EventBuilder, Keys } from \"../core/index.ts\";\nimport type { Event } from \"../core/index.ts\";\nimport type { EventStore } from \"../storage/types.ts\";\n\nexport { type Filter } from \"../core/filter.ts\";\n\nexport type EventStoreConformanceCase = {\n name: string;\n run: (store: EventStore) => Promise<void>;\n};\n\nconst ALICE_SK = \"0000000000000000000000000000000000000000000000000000000000000101\";\nconst BOB_SK = \"0000000000000000000000000000000000000000000000000000000000000102\";\n\nfunction alice(): Keys {\n return Keys.fromSecretKey(ALICE_SK);\n}\nfunction bob(): Keys {\n return Keys.fromSecretKey(BOB_SK);\n}\n\nfunction note(keys: Keys, content: string, createdAt: number, tags: string[][] = []): Event {\n return new EventBuilder(1, content).tags(tags).createdAt(createdAt).signWithKeys(keys);\n}\n\nfunction replaceable(keys: Keys, kind: number, content: string, createdAt: number): Event {\n return new EventBuilder(kind, content).createdAt(createdAt).signWithKeys(keys);\n}\n\nfunction addressable(\n keys: Keys,\n kind: number,\n d: string,\n content: string,\n createdAt: number,\n): Event {\n return new EventBuilder(kind, content)\n .tags([[\"d\", d]])\n .createdAt(createdAt)\n .signWithKeys(keys);\n}\n\nfunction fail(message: string): never {\n throw new Error(`EventStore conformance: ${message}`);\n}\n\nfunction eq<T>(got: T, want: T, what: string): void {\n if (JSON.stringify(got) !== JSON.stringify(want)) {\n fail(`${what}: expected ${JSON.stringify(want)}, got ${JSON.stringify(got)}`);\n }\n}\n\nfunction ids(events: ReadonlyArray<Event>): string[] {\n return events.map((e) => e.id);\n}\n\n/**\n * Framework-agnostic {@link EventStore} conformance cases. Each `run` throws on failure; register\n * them with the test framework of your choice: `for (const c of eventStoreConformanceCases)\n * test(c.name, () => c.run(store()))`.\n */\nexport const eventStoreConformanceCases: ReadonlyArray<EventStoreConformanceCase> = [\n {\n name: \"put accepted then duplicate\",\n async run(store) {\n const e = note(alice(), \"hello\", 1);\n eq(await store.put(e), \"accepted\", \"first put\");\n eq(await store.put(e), \"duplicate\", \"second put\");\n eq(await store.get(e.id), e, \"get after put\");\n eq(\n await store.putMany([note(alice(), \"a\", 2), note(alice(), \"b\", 3)]),\n [\"accepted\", \"accepted\"],\n \"putMany results\",\n );\n },\n },\n {\n name: \"ephemeral event returns ephemeral and is not stored\",\n async run(store) {\n const e = replaceable(alice(), 20001, \"ephemeral\", 1);\n eq(await store.put(e), \"ephemeral\", \"put ephemeral\");\n eq(await store.get(e.id), undefined, \"ephemeral not retrievable\");\n eq(await store.query([{ kinds: [20001] }]), [], \"ephemeral not queryable\");\n eq(await store.count([{ kinds: [20001] }]), 0, \"ephemeral not counted\");\n },\n },\n {\n name: \"replaceable event keeps newest and rejects stale\",\n async run(store) {\n const older = replaceable(alice(), 0, \"old\", 1);\n const newer = replaceable(alice(), 0, \"new\", 2);\n eq(await store.put(older), \"accepted\", \"first replaceable\");\n eq(await store.put(newer), \"replaced\", \"newer replaces\");\n eq(await store.put(older), \"rejected\", \"stale replaceable rejected\");\n eq(await store.get(older.id), undefined, \"replaced event gone\");\n eq(\n ids(await store.query([{ kinds: [0], authors: [alice().publicKey] }])),\n [newer.id],\n \"newest kept\",\n );\n },\n },\n {\n name: \"addressable event replaces by address\",\n async run(store) {\n const v1 = addressable(alice(), 30001, \"x\", \"v1\", 1);\n const v2 = addressable(alice(), 30001, \"x\", \"v2\", 2);\n const other = addressable(alice(), 30001, \"y\", \"other\", 3);\n eq(await store.put(v1), \"accepted\", \"first addressable\");\n eq(await store.put(v2), \"replaced\", \"same address replaced\");\n eq(await store.put(v1), \"rejected\", \"stale addressable rejected\");\n eq(await store.put(other), \"accepted\", \"different address independent\");\n eq(await store.get(v1.id), undefined, \"v1 gone\");\n eq(\n ids(await store.query([{ kinds: [30001], authors: [alice().publicKey] }])).sort(),\n [other.id, v2.id].sort(),\n \"independent addresses\",\n );\n },\n },\n {\n name: \"deletion removes same-author e-tag targets only\",\n async run(store) {\n const mine = note(alice(), \"mine\", 1);\n const theirs = note(bob(), \"theirs\", 1);\n await store.put(mine);\n await store.put(theirs);\n const delForeign = EventBuilder.deletion([theirs.id]).createdAt(2).signWithKeys(alice());\n eq(await store.put(delForeign), \"deleted\", \"kind 5 accepted\");\n eq(await store.get(theirs.id), theirs, \"other author's event survives\");\n const delMine = EventBuilder.deletion([mine.id]).createdAt(3).signWithKeys(alice());\n eq(await store.put(delMine), \"deleted\", \"kind 5 accepted\");\n eq(await store.get(mine.id), undefined, \"own event deleted\");\n eq(await store.query([{ ids: [mine.id] }]), [], \"deleted event not queryable\");\n eq(await store.put(mine), \"duplicate\", \"deleted event cannot be re-put\");\n },\n },\n {\n name: \"deletion removes addressable via a tag and tombstones re-put\",\n async run(store) {\n const target = addressable(alice(), 30001, \"z\", \"target\", 1);\n await store.put(target);\n const coord = `30001:${alice().publicKey}:z`;\n const del = EventBuilder.deletion([{ address: coord }], \"\")\n .createdAt(2)\n .signWithKeys(alice());\n eq(await store.put(del), \"deleted\", \"a-tag deletion\");\n eq(await store.get(target.id), undefined, \"addressable deleted\");\n const stillCovered = addressable(alice(), 30001, \"z\", \"covered\", 1);\n eq(await store.put(stillCovered), \"duplicate\", \"tombstoned address rejects re-put\");\n eq(await store.query([{ kinds: [30001], authors: [alice().publicKey] }]), [], \"none left\");\n const after = addressable(alice(), 30001, \"z\", \"after\", 3);\n eq(await store.put(after), \"accepted\", \"events newer than the deletion are kept\");\n },\n },\n {\n name: \"query orders newest first and applies limit\",\n async run(store) {\n const a = note(alice(), \"a\", 1);\n const b = note(alice(), \"b\", 3);\n const c = note(alice(), \"c\", 2);\n await store.putMany([a, b, c]);\n eq(\n ids(await store.query([{ kinds: [1], authors: [alice().publicKey] }])),\n [b.id, c.id, a.id],\n \"descending created_at\",\n );\n eq(\n ids(await store.query([{ kinds: [1], authors: [alice().publicKey], limit: 2 }])),\n [b.id, c.id],\n \"limit applies\",\n );\n },\n },\n {\n name: \"query filters by kinds, authors, #e, #p, since, until\",\n async run(store) {\n const target = note(bob(), \"target\", 1);\n const tagged = note(alice(), \"tagged\", 2, [\n [\"e\", target.id],\n [\"p\", bob().publicKey],\n ]);\n const plain = note(alice(), \"plain\", 3);\n await store.putMany([target, tagged, plain]);\n const all = await store.query([{ kinds: [1] }]);\n eq(all.length, 3, \"kind filter\");\n eq(ids(await store.query([{ authors: [bob().publicKey] }])), [target.id], \"authors\");\n eq(ids(await store.query([{ \"#e\": [target.id] }])), [tagged.id], \"#e\");\n eq(ids(await store.query([{ \"#p\": [bob().publicKey] }])), [tagged.id], \"#p\");\n eq(ids(await store.query([{ kinds: [1], since: 2 }])), [plain.id, tagged.id], \"since\");\n eq(ids(await store.query([{ kinds: [1], until: 2 }])), [tagged.id, target.id], \"until\");\n eq(\n ids(await store.query([{ ids: [target.id, tagged.id] }])).sort(),\n [tagged.id, target.id].sort(),\n \"ids filter\",\n );\n },\n },\n {\n name: \"multi-letter # keys are ignored (NIP-01 single-letter tag conditions only)\",\n async run(store) {\n const tagged = note(alice(), \"tagged\", 1, [[\"client\", \"test-app\"]]);\n const plain = note(bob(), \"plain\", 2);\n await store.putMany([tagged, plain]);\n eq(await store.count([{ \"#client\": [\"test-app\"] }]), 2, \"ignored on count\");\n eq(await store.count([{ \"#client\": [\"other\"] }]), 2, \"ignored regardless of value\");\n eq(\n ids(await store.query([{ \"#client\": [\"test-app\"] }])).sort(),\n [plain.id, tagged.id].sort(),\n \"ignored on query\",\n );\n },\n },\n {\n name: \"count matches query cardinality across overlapping filters\",\n async run(store) {\n const a = note(alice(), \"a\", 1);\n const b = note(bob(), \"b\", 2);\n await store.putMany([a, b]);\n eq(await store.count([{ kinds: [1] }]), 2, \"count all\");\n eq(\n await store.count([\n { authors: [alice().publicKey] },\n { authors: [bob().publicKey] },\n { ids: [a.id] },\n ]),\n 2,\n \"count dedupes union\",\n );\n eq(await store.count([{ kinds: [2] }]), 0, \"count none\");\n },\n },\n {\n name: \"negentropyItems sorted by created_at then id and respects limit\",\n async run(store) {\n const a = note(alice(), \"a\", 2);\n const b = note(alice(), \"b\", 1);\n const c = note(bob(), \"c\", 3);\n await store.putMany([a, b, c]);\n const items = await store.negentropyItems({ kinds: [1] });\n eq(\n items.map((i) => i.id),\n [b.id, a.id, c.id],\n \"created_at asc ordering\",\n );\n const limited = await store.negentropyItems({ kinds: [1], limit: 2 });\n eq(\n limited.map((i) => i.id),\n [a.id, c.id],\n \"limit keeps newest two, presented ascending\",\n );\n eq(await store.negentropyItems({ kinds: [2] }), [], \"no match\");\n const filtered = await store.negentropyItems({ authors: [bob().publicKey] });\n eq(filtered, [{ id: c.id, created_at: 3 }], \"filter\");\n },\n },\n {\n name: \"outbox bounds roundtrip\",\n async run(store) {\n eq(await store.getOutboxBound(alice().publicKey, 1), undefined, \"unset bound\");\n await store.setOutboxBound(alice().publicKey, 1, { oldest: 10, newest: 20 });\n eq(\n await store.getOutboxBound(alice().publicKey, 1),\n { oldest: 10, newest: 20 },\n \"bound read\",\n );\n await store.setOutboxBound(alice().publicKey, 1, { oldest: 5, newest: 30 });\n eq(\n await store.getOutboxBound(alice().publicKey, 1),\n { oldest: 5, newest: 30 },\n \"bound overwrite\",\n );\n },\n },\n {\n name: \"remove deletes by id and tombstones\",\n async run(store) {\n const a = note(alice(), \"a\", 1);\n await store.put(a);\n eq(await store.remove([a.id]), 1, \"remove count\");\n eq(await store.get(a.id), undefined, \"removed\");\n eq(await store.remove([a.id]), 0, \"second remove is noop\");\n eq(await store.put(a), \"duplicate\", \"removed id tombstoned\");\n },\n },\n {\n name: \"clear wipes events and bounds\",\n async run(store) {\n const a = note(alice(), \"a\", 1);\n await store.put(a);\n await store.setOutboxBound(alice().publicKey, 1, { oldest: 1, newest: 1 });\n await store.clear();\n eq(await store.get(a.id), undefined, \"event gone\");\n eq(await store.query([{ kinds: [1] }]), [], \"query empty\");\n eq(await store.getOutboxBound(alice().publicKey, 1), undefined, \"bound gone\");\n eq(await store.put(a), \"accepted\", \"store usable after clear\");\n },\n },\n];\n","import { Keys, finalizeEvent } from \"../core/index.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { isTag } from \"../core/tag.ts\";\nimport { isRecord, nowSeconds } from \"../core/util.ts\";\nimport {\n getConversationKey,\n decrypt as nip44Decrypt,\n encrypt as nip44Encrypt,\n} from \"../nips/nip44.ts\";\nimport { decodeNip46Request, encodeNip46Response } from \"../nips/nip46.ts\";\nimport type { Nip46Response } from \"../nips/nip46.ts\";\nimport type { FakeRelayNetwork } from \"./network.ts\";\n\nexport type FakeNip46SignerOptions = {\n /** The network the fake signer joins as a client of `relayUrl`. */\n network: FakeRelayNetwork;\n relayUrl: string;\n /** Pubkey of the NIP-46 client under test. */\n clientPubkey: string;\n /** Remote-signer key; random when omitted. */\n bunkerSk?: string | undefined;\n /** The user key the signer signs for; random when omitted. */\n userSk?: string | undefined;\n /** First answer `auth_url` (result) with this URL in the error field, then the real response. */\n authUrl?: string | undefined;\n authUrlMethods?: ReadonlyArray<string> | undefined;\n /** Delay in ms before the real response after an `auth_url` reply. */\n authReplyDelayMs?: number | undefined;\n /** Collected RPC requests (mutated as they arrive). */\n requests?: Array<{ method: string; params: ReadonlyArray<string> }> | undefined;\n /** `switch_relays` result. Default `\"null\"`. */\n switchRelays?: string[] | undefined;\n /** Override `connect` RPC result. Default `\"ack\"`. */\n connectResult?: string | undefined;\n /** Reply with neither `result` nor `error` for these methods. */\n emptyMethods?: ReadonlyArray<string> | undefined;\n /**\n * Serialize the response JSON; default {@link encodeNip46Response}, which omits absent fields.\n * Lets tests put explicit `null`s on the wire like some remote signers do.\n */\n encodeResponse?: ((res: Nip46Response) => string) | undefined;\n};\n\nexport type FakeNip46Signer = {\n readonly bunkerPubkey: string;\n readonly userPublicKey: string;\n /** Also answer RPCs on another relay of the same network (e.g. after switch_relays). */\n attach: (relayUrl: string) => void;\n /** Publish the nostrconnect handshake secret confirmation as the bunker. */\n confirmHandshake: (secret: string) => void;\n close: () => void;\n};\n\n/**\n * In-process NIP-46 remote signer: subscribes to kind:24133 requests on `relayUrl` and answers as a\n * bunker, encrypted to `clientPubkey` with NIP-44.\n */\nexport function createFakeNip46Signer(opts: FakeNip46SignerOptions): FakeNip46Signer {\n const bunkerKeys =\n opts.bunkerSk !== undefined && opts.bunkerSk !== \"\"\n ? Keys.fromSecretKey(opts.bunkerSk)\n : Keys.generate();\n const userKeys =\n opts.userSk !== undefined && opts.userSk !== \"\"\n ? Keys.fromSecretKey(opts.userSk)\n : Keys.generate();\n const convKey = getConversationKey(bunkerKeys.secretKey.bytes, opts.clientPubkey);\n const handled = new Set<string>();\n const authPending = new Set<string>();\n const authMethods = new Set(opts.authUrlMethods ?? [\"connect\"]);\n let closed = false;\n\n const sockets = new Set<{ close: () => void }>();\n const listen = (relayUrl: string): void => {\n const ws = new opts.network.websocketImplementation(relayUrl);\n ws.addEventListener(\"open\", () => {\n ws.send(\n JSON.stringify([\n \"REQ\",\n \"fake-nip46-signer\",\n { kinds: [Kind.NostrConnect], \"#p\": [bunkerKeys.publicKey] },\n ]),\n );\n });\n ws.addEventListener(\"message\", (ev) => onMessage(ev, ws));\n sockets.add(ws);\n };\n\n const onMessage = (ev: unknown, ws: { send: (data: string) => void }): void => {\n if (typeof ev !== \"object\" || ev === null || !(\"data\" in ev)) {\n return;\n }\n const { data } = ev;\n let msg: unknown;\n try {\n msg = JSON.parse(String(data));\n } catch {\n return;\n }\n if (!Array.isArray(msg) || msg[0] !== \"EVENT\") {\n return;\n }\n const parts: unknown[] = msg;\n const rawEvent = parts.at(2);\n if (!isRecord(rawEvent)) {\n return;\n }\n const { kind, pubkey, content, id } = rawEvent;\n if (kind !== Kind.NostrConnect) {\n return;\n }\n if (typeof pubkey !== \"string\" || typeof content !== \"string\" || typeof id !== \"string\") {\n return;\n }\n if (pubkey !== opts.clientPubkey || handled.has(id)) {\n return;\n }\n\n try {\n const req = decodeNip46Request(nip44Decrypt(content, convKey));\n if (\n opts.authUrl !== undefined &&\n opts.authUrl !== \"\" &&\n authMethods.has(req.method) &&\n !authPending.has(req.id)\n ) {\n authPending.add(req.id);\n reply(ws, opts.clientPubkey, req.id, \"auth_url\", opts.authUrl);\n }\n handled.add(id);\n opts.requests?.push({ method: req.method, params: req.params });\n\n let result: string | undefined;\n let error: string | undefined;\n switch (opts.emptyMethods?.includes(req.method) === true ? \"#empty\" : req.method) {\n case \"connect\":\n result = opts.connectResult ?? \"ack\";\n break;\n case \"get_public_key\":\n result = userKeys.publicKey;\n break;\n case \"ping\":\n result = \"pong\";\n break;\n case \"switch_relays\":\n result = opts.switchRelays === undefined ? \"null\" : JSON.stringify(opts.switchRelays);\n break;\n case \"logout\":\n result = \"ack\";\n break;\n case \"sign_event\": {\n const rawTemplate: unknown = JSON.parse(req.params.at(0) ?? \"null\");\n if (\n !isRecord(rawTemplate) ||\n typeof rawTemplate[\"kind\"] !== \"number\" ||\n !Array.isArray(rawTemplate[\"tags\"]) ||\n !rawTemplate[\"tags\"].every(isTag) ||\n typeof rawTemplate[\"content\"] !== \"string\" ||\n typeof rawTemplate[\"created_at\"] !== \"number\"\n ) {\n throw new Error(\"invalid sign_event template\");\n }\n result = JSON.stringify(\n finalizeEvent(\n {\n kind: rawTemplate[\"kind\"],\n tags: rawTemplate[\"tags\"],\n content: rawTemplate[\"content\"],\n created_at: rawTemplate[\"created_at\"],\n },\n userKeys.secretKey,\n ),\n );\n break;\n }\n case \"#empty\":\n break;\n default:\n error = `unsupported method ${req.method}`;\n }\n if (authPending.has(req.id) && opts.authReplyDelayMs !== undefined) {\n const pendingId = req.id;\n const pendingResult = result;\n const pendingError = error;\n setTimeout(\n () => reply(ws, opts.clientPubkey, pendingId, pendingResult, pendingError),\n opts.authReplyDelayMs,\n );\n } else {\n reply(ws, opts.clientPubkey, req.id, result, error);\n }\n } catch {\n handled.add(id);\n }\n };\n\n const reply = (\n ws: { send: (data: string) => void },\n clientPubkey: string,\n id: string,\n result?: string,\n error?: string,\n ): void => {\n if (closed) {\n return;\n }\n const payload = (opts.encodeResponse ?? encodeNip46Response)({ id, result, error });\n const event = finalizeEvent(\n {\n kind: Kind.NostrConnect,\n tags: [[\"p\", clientPubkey]],\n content: nip44Encrypt(payload, convKey),\n created_at: nowSeconds(),\n },\n bunkerKeys.secretKey,\n );\n ws.send(JSON.stringify([\"EVENT\", event]));\n };\n\n listen(opts.relayUrl);\n\n return {\n bunkerPubkey: bunkerKeys.publicKey,\n userPublicKey: userKeys.publicKey,\n confirmHandshake(secret: string): void {\n const payload = encodeNip46Response({ id: \"handshake\", result: secret });\n const event = finalizeEvent(\n {\n kind: Kind.NostrConnect,\n tags: [[\"p\", opts.clientPubkey]],\n content: nip44Encrypt(payload, convKey),\n created_at: nowSeconds(),\n },\n bunkerKeys.secretKey,\n );\n opts.network.relay(opts.relayUrl).inject(event);\n },\n attach(relayUrl: string): void {\n listen(relayUrl);\n },\n close(): void {\n closed = true;\n for (const ws of sockets) {\n ws.close();\n }\n sockets.clear();\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"testing.mjs","names":["parts","nip44Decrypt","nip44Encrypt"],"sources":["../src/testing/relay-core.ts","../src/testing/network.ts","../src/testing/serve.ts","../src/testing/conformance.ts","../src/testing/nip46.ts"],"sourcesContent":["import { errorMessage } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { compareEventsDesc, validateSignedEvent } from \"../core/event.ts\";\nimport { matchFilter } from \"../core/filter.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { bytesToHex, isRecord, normalizeURL } from \"../core/util.ts\";\nimport { verifyEvent } from \"../core/verifier.ts\";\nimport { Negentropy, PROTOCOL_VERSION, storageFromEvents } from \"../nips/nip77.ts\";\nimport { MemoryEventStore } from \"../storage/memory.ts\";\nimport type { PutResult } from \"../storage/types.ts\";\n\nexport type FakeRelayOptions = {\n /** Delay before every relay→client message. */\n latencyMs?: number | undefined;\n auth?:\n | {\n /** Sent as `[\"AUTH\", challenge]` right after the socket opens. */\n challenge: string;\n /** REQ whose filters can match these kinds is CLOSED `auth-required:` until AUTH succeeds. */\n readKinds?: ReadonlyArray<number> | undefined;\n /** EVENT → `[\"OK\", id, false, \"auth-required: ...\"]` until AUTH succeeds. */\n writes?: boolean | undefined;\n }\n | undefined;\n /** EVENT → `[\"OK\", id, false, \"rate-limited: ...\"]`; nothing is stored. */\n rateLimited?: boolean | undefined;\n /** Out-of-order reply: send EOSE first, then the stored events. */\n eoseBeforeEvents?: boolean | undefined;\n};\n\nexport type FakeRelay = {\n readonly url: string;\n seed: (events: ReadonlyArray<Event>) => void;\n events: () => ReadonlyArray<Event>;\n /**\n * Store an event and broadcast it to matching subscriptions. Unlike client EVENT writes the relay\n * does not verify it here, so forged events (mismatched id/signature) are delivered as-is.\n */\n inject: (event: Event) => void;\n /**\n * Push an event to matching subscriptions without storing it: a resubscription never re-serves\n * it. Use it for forged events (mismatched id/signature) when exercising client-side verification\n * failure handling.\n */\n deliver: (event: Event) => void;\n /**\n * Live subscriptions whose filters match the event and whose initial dump already finished (their\n * EOSE is on the wire). A publish that waits for this to be non-zero always lands on the client's\n * live path — an earlier event is just another initial-page row.\n */\n matchingLiveSubscribers: (event: Event) => number;\n disconnect: () => void;\n closeSubscriptions: (reason: string) => void;\n configure: (opts: Partial<FakeRelayOptions>) => void;\n /** Every parsed client→relay message received, in arrival order. */\n clientMessages: () => ReadonlyArray<unknown>;\n};\n\n/** One relay→client transport (an in-process socket or a `ws` connection). */\nexport type RelayTransport = {\n send: (data: string) => void;\n close: () => void;\n};\n\nexport type FakeRelaySession = {\n transport: RelayTransport;\n subs: Map<string, Filter[]>;\n /** REQ ids whose initial dump finished — EOSE already sent to the client. */\n eosed: Set<string>;\n negs: Map<string, Negentropy>;\n authed: boolean;\n queue: Promise<void>;\n /** Armed latency-delayed sends; cleared on detach so nothing fires post-close. */\n timers: Set<ReturnType<typeof setTimeout>>;\n};\n\n/** Wire filters are trusted as NIP-01 filters once they are records. */\nfunction isFilter(value: unknown): value is Filter {\n return isRecord(value);\n}\n\nfunction searchMatch(filter: Filter, event: Event): boolean {\n if (filter.search === undefined) {\n return true;\n }\n return event.content.toLowerCase().includes(filter.search.toLowerCase());\n}\n\nfunction subMatches(filters: ReadonlyArray<Filter>, event: Event): boolean {\n return filters.some((f) => matchFilter(f, event) && searchMatch(f, event));\n}\n\n/** Put outcomes that made the event part of the relay's live stream. */\nfunction isLivePut(result: PutResult): boolean {\n return (\n result === \"accepted\" || result === \"replaced\" || result === \"deleted\" || result === \"ephemeral\"\n );\n}\n\nfunction mayReadKinds(filters: ReadonlyArray<Filter>, readKinds: ReadonlyArray<number>): boolean {\n return filters.some((f) => f.kinds === undefined || f.kinds.some((k) => readKinds.includes(k)));\n}\n\n/**\n * Transport-agnostic fake relay: one NIP-01/42/45/50/77 session state machine per connection,\n * storage via {@link MemoryEventStore}. No timers except `latencyMs`; no global state.\n */\nexport class FakeRelayCore implements FakeRelay {\n readonly url: string;\n #opts: FakeRelayOptions;\n readonly #store = new MemoryEventStore();\n readonly #sessions = new Set<FakeRelaySession>();\n #mirror = new Map<string, Event>();\n readonly #inbox: unknown[] = [];\n #pending: Promise<void> = Promise.resolve();\n\n constructor(url: string, opts: FakeRelayOptions = {}) {\n this.url = normalizeURL(url);\n this.#opts = opts;\n }\n\n configure(opts: Partial<FakeRelayOptions>): void {\n this.#opts = { ...this.#opts, ...opts };\n }\n\n seed(events: ReadonlyArray<Event>): void {\n for (const event of events) {\n this.#mirror.set(event.id, event);\n }\n this.#pending = (async (): Promise<void> => {\n await this.#pending;\n for (const event of events) {\n // Sequential puts keep per-event store ordering for replaceables.\n // oxlint-disable-next-line eslint/no-await-in-loop\n await this.#store.put(event);\n }\n await this.#refreshMirror();\n })();\n }\n\n events(): ReadonlyArray<Event> {\n return [...this.#mirror.values()];\n }\n\n inject(event: Event): void {\n this.#pending = (async (): Promise<void> => {\n await this.#pending;\n const result = await this.#store.put(event);\n await this.#refreshMirror();\n if (isLivePut(result)) {\n this.#deliver(event);\n }\n })();\n }\n\n deliver(event: Event): void {\n this.#pending = (async (): Promise<void> => {\n await this.#pending;\n this.#deliver(event);\n })();\n }\n\n matchingLiveSubscribers(event: Event): number {\n let count = 0;\n for (const session of this.#sessions) {\n for (const id of session.eosed) {\n const filters = session.subs.get(id);\n if (filters !== undefined && subMatches(filters, event)) {\n count += 1;\n }\n }\n }\n return count;\n }\n\n disconnect(): void {\n const sessions: FakeRelaySession[] = [...this.#sessions];\n this.#sessions.clear();\n for (const session of sessions) {\n session.transport.close();\n }\n }\n\n closeSubscriptions(reason: string): void {\n for (const session of this.#sessions) {\n for (const id of session.subs.keys()) {\n this.#send(session, [\"CLOSED\", id, reason]);\n }\n session.subs.clear();\n session.eosed.clear();\n session.negs.clear();\n }\n }\n\n clientMessages(): ReadonlyArray<unknown> {\n return this.#inbox;\n }\n\n /** Attach a transport; returns a handle for {@link handleMessage}/detach. */\n connect(transport: RelayTransport): FakeRelaySession {\n const session: FakeRelaySession = {\n transport,\n subs: new Map(),\n eosed: new Set(),\n negs: new Map(),\n authed: false,\n queue: Promise.resolve(),\n timers: new Set(),\n };\n this.#sessions.add(session);\n if (this.#opts.auth) {\n this.#send(session, [\"AUTH\", this.#opts.auth.challenge]);\n }\n return session;\n }\n\n detach(session: FakeRelaySession): void {\n this.#sessions.delete(session);\n for (const timer of session.timers) {\n clearTimeout(timer);\n }\n session.timers.clear();\n }\n\n /** Feed a raw client→relay frame. Messages of one session are handled in order. */\n handleMessage(session: FakeRelaySession, raw: string): void {\n // A handler throw must not poison the session queue: NOTICE and continue.\n session.queue = (async (): Promise<void> => {\n try {\n await session.queue;\n await this.#handle(session, raw);\n } catch (error) {\n const message = errorMessage(error);\n try {\n this.#send(session, [\"NOTICE\", `error: ${message}`]);\n } catch {\n // transport gone\n }\n }\n })();\n }\n\n async #refreshMirror(): Promise<void> {\n const all = await this.#store.query([{}]);\n this.#mirror = new Map(all.map((e) => [e.id, e]));\n }\n\n #send(session: FakeRelaySession, message: unknown[]): void {\n const payload = JSON.stringify(message);\n const { latencyMs } = this.#opts;\n if (latencyMs !== undefined && latencyMs > 0) {\n const timer = setTimeout(() => {\n session.timers.delete(timer);\n // A session detached during the latency window must not emit on a dead\n // transport.\n if (!this.#sessions.has(session)) {\n return;\n }\n session.transport.send(payload);\n }, latencyMs);\n session.timers.add(timer);\n return;\n }\n session.transport.send(payload);\n }\n\n #deliver(event: Event): void {\n for (const session of this.#sessions) {\n for (const [id, filters] of session.subs) {\n if (subMatches(filters, event)) {\n this.#send(session, [\"EVENT\", id, event]);\n }\n }\n }\n }\n\n async #matched(filters: Filter[]): Promise<Event[]> {\n // Per-filter NIP-01 limit: query unbounded, apply search, keep that\n // filter's newest — never a cross-filter minimum.\n const keptPerFilter = await Promise.all(\n filters.map(async (filter): Promise<Event[]> => {\n const unbounded = { ...filter };\n delete unbounded.limit;\n const queried = await this.#store.query([unbounded]);\n const rows = queried.filter((event) => searchMatch(filter, event));\n rows.sort(compareEventsDesc);\n return filter.limit === undefined ? rows : rows.slice(0, filter.limit);\n }),\n );\n const seen = new Map<string, Event>();\n for (const kept of keptPerFilter) {\n for (const event of kept) {\n if (!seen.has(event.id)) {\n seen.set(event.id, event);\n }\n }\n }\n const matched = [...seen.values()];\n matched.sort(compareEventsDesc);\n return matched;\n }\n\n async #handle(session: FakeRelaySession, raw: string): Promise<void> {\n await this.#pending;\n let msg: unknown;\n try {\n msg = JSON.parse(raw);\n } catch {\n this.#send(session, [\"NOTICE\", \"error: invalid message\"]);\n return;\n }\n if (!Array.isArray(msg)) {\n this.#send(session, [\"NOTICE\", \"error: invalid message\"]);\n return;\n }\n const items: unknown[] = msg;\n const [type] = items;\n if (typeof type !== \"string\") {\n this.#send(session, [\"NOTICE\", \"error: invalid message\"]);\n return;\n }\n this.#inbox.push(msg);\n\n switch (type) {\n case \"EVENT\": {\n const [, rawEvent] = items;\n if (!isRecord(rawEvent) || typeof rawEvent[\"id\"] !== \"string\") {\n this.#send(session, [\"NOTICE\", \"invalid: malformed event\"]);\n return;\n }\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- wire record trusted as Event; verifyEvent below is the authoritative check\n const event = rawEvent as Event;\n if (this.#opts.rateLimited === true) {\n this.#send(session, [\"OK\", event.id, false, \"rate-limited: slow down\"]);\n return;\n }\n if (this.#opts.auth?.writes === true && !session.authed) {\n this.#send(session, [\"OK\", event.id, false, \"auth-required: login first\"]);\n return;\n }\n if (!verifyEvent(event)) {\n this.#send(session, [\"OK\", event.id, false, \"invalid: signature or id\"]);\n return;\n }\n const result = await this.#store.put(event);\n await this.#refreshMirror();\n if (result === \"rejected\") {\n this.#send(session, [\n \"OK\",\n event.id,\n false,\n \"invalid: a newer version of this event exists\",\n ]);\n return;\n }\n if (result === \"invalid\") {\n this.#send(session, [\"OK\", event.id, false, \"invalid: malformed event\"]);\n return;\n }\n if (result === \"duplicate\") {\n this.#send(session, [\"OK\", event.id, true, \"duplicate: already have this event\"]);\n return;\n }\n this.#send(session, [\"OK\", event.id, true, \"\"]);\n this.#deliver(event);\n return;\n }\n case \"REQ\": {\n const [, id] = items;\n const filters = items.slice(2);\n if (typeof id !== \"string\" || filters.length === 0 || !filters.every(isFilter)) {\n this.#send(session, [\"NOTICE\", \"error: invalid REQ\"]);\n return;\n }\n const readKinds = this.#opts.auth?.readKinds;\n if (readKinds !== undefined && !session.authed && mayReadKinds(filters, readKinds)) {\n this.#send(session, [\"CLOSED\", id, \"auth-required: we only serve to authed users\"]);\n session.subs.delete(id);\n session.eosed.delete(id);\n return;\n }\n session.subs.set(id, filters);\n session.eosed.delete(id);\n const matched = await this.#matched(filters);\n if (this.#opts.eoseBeforeEvents === true) {\n this.#send(session, [\"EOSE\", id]);\n session.eosed.add(id);\n }\n for (const event of matched) {\n this.#send(session, [\"EVENT\", id, event]);\n }\n if (this.#opts.eoseBeforeEvents !== true) {\n this.#send(session, [\"EOSE\", id]);\n session.eosed.add(id);\n }\n return;\n }\n case \"CLOSE\": {\n const [, id] = items;\n if (typeof id === \"string\") {\n session.subs.delete(id);\n session.eosed.delete(id);\n }\n return;\n }\n case \"COUNT\": {\n const [, id] = items;\n const filters = items.slice(2);\n if (typeof id !== \"string\" || !filters.every(isFilter)) {\n this.#send(session, [\"NOTICE\", \"error: invalid COUNT\"]);\n return;\n }\n const matched = await this.#matched(filters);\n this.#send(session, [\"COUNT\", id, { count: matched.length }]);\n return;\n }\n case \"AUTH\": {\n const { auth } = this.#opts;\n const [, rawAuth] = items;\n if (!validateSignedEvent(rawAuth)) {\n this.#send(session, [\"OK\", \"0\".repeat(64), false, \"error: invalid auth event\"]);\n return;\n }\n const challengeTag = rawAuth.tags.find((t) => t[0] === \"challenge\")?.[1];\n const relayTag = rawAuth.tags.find((t) => t[0] === \"relay\")?.[1];\n let relayOk = false;\n if (relayTag !== undefined) {\n try {\n relayOk = normalizeURL(relayTag) === this.url;\n } catch {\n // unparseable relay tag\n }\n }\n if (\n auth !== undefined &&\n rawAuth.kind === Kind.ClientAuth &&\n verifyEvent(rawAuth) &&\n challengeTag === auth.challenge &&\n relayOk\n ) {\n session.authed = true;\n this.#send(session, [\"OK\", rawAuth.id, true, \"\"]);\n return;\n }\n this.#send(session, [\"OK\", rawAuth.id, false, \"error: invalid auth event\"]);\n return;\n }\n case \"NEG-OPEN\": {\n if (items.length === 5) {\n this.#send(session, [\"NEG-ERR\", items[1], \"error: obsolete 5-element NEG-OPEN\"]);\n return;\n }\n const [, id, rawFilter, negMessage] = items;\n if (\n items.length !== 4 ||\n typeof id !== \"string\" ||\n !isFilter(rawFilter) ||\n typeof negMessage !== \"string\"\n ) {\n const badId = typeof id === \"string\" ? id : \"\";\n this.#send(session, [\"NEG-ERR\", badId, \"error: invalid NEG-OPEN\"]);\n return;\n }\n // The NIP-77 filter is a plain NIP-01 filter — `limit` applies.\n const matched = await this.#matched([rawFilter]);\n const neg = new Negentropy(storageFromEvents(matched));\n let opened: string | undefined;\n try {\n opened = neg.reconcile(negMessage).nextMessage ?? undefined;\n } catch {\n this.#send(session, [\"NEG-ERR\", id, \"error: invalid negentropy message\"]);\n return;\n }\n session.negs.set(id, neg);\n this.#send(session, [\n \"NEG-MSG\",\n id,\n opened ?? bytesToHex(new Uint8Array([PROTOCOL_VERSION])),\n ]);\n return;\n }\n case \"NEG-MSG\": {\n const [, id, negMessage] = items;\n if (items.length !== 3 || typeof id !== \"string\" || typeof negMessage !== \"string\") {\n return;\n }\n const handle = session.negs.get(id);\n if (handle === undefined) {\n this.#send(session, [\"NEG-ERR\", id, \"closed: unknown subscription\"]);\n return;\n }\n let next: string | undefined;\n try {\n next = handle.reconcile(negMessage).nextMessage ?? undefined;\n } catch {\n session.negs.delete(id);\n this.#send(session, [\"NEG-ERR\", id, \"error: invalid negentropy message\"]);\n return;\n }\n this.#send(session, [\n \"NEG-MSG\",\n id,\n next ?? bytesToHex(new Uint8Array([PROTOCOL_VERSION])),\n ]);\n return;\n }\n case \"NEG-CLOSE\": {\n const [, id] = items;\n if (typeof id === \"string\") {\n session.negs.delete(id);\n }\n return;\n }\n default:\n this.#send(session, [\"NOTICE\", `error: unsupported ${type}`]);\n }\n }\n}\n","import { normalizeURL } from \"../core/util.ts\";\nimport type { WebSocketConstructor } from \"../relay/websocket.ts\";\nimport { FakeRelayCore } from \"./relay-core.ts\";\nimport type { FakeRelay, FakeRelayOptions, RelayTransport } from \"./relay-core.ts\";\n\nexport { type FakeRelay, type FakeRelayOptions } from \"./relay-core.ts\";\nexport type FakeRelayNetwork = {\n /** Pass to `Client` / `Pool` / `Relay` as `websocketImplementation`. */\n readonly websocketImplementation: WebSocketConstructor;\n /** Get-or-create a relay handle by normalized URL. */\n relay: (url: string, opts?: FakeRelayOptions) => FakeRelay;\n close: () => void;\n};\n\ntype Listener = (ev: unknown) => void;\n\n/**\n * In-process fake relay network. `new websocketImplementation(url)` opens a socket bound to the\n * network's relay for that URL; `send` is dispatched to the relay session directly (no polling).\n */\nexport function createFakeRelayNetwork(defaults: FakeRelayOptions = {}): FakeRelayNetwork {\n const cores = new Map<string, FakeRelayCore>();\n\n const coreFor = (url: string, opts?: FakeRelayOptions): FakeRelayCore => {\n const key = normalizeURL(url);\n let core = cores.get(key);\n if (!core) {\n core = new FakeRelayCore(key, { ...defaults, ...opts });\n cores.set(key, core);\n } else if (opts) {\n core.configure(opts);\n }\n return core;\n };\n\n class FakeSocket {\n static readonly OPEN = 1;\n static readonly CONNECTING = 0;\n static readonly CLOSING = 2;\n static readonly CLOSED = 3;\n\n readonly url: string;\n /** Client→relay frames, for tests that inspect wire traffic. */\n readonly sent: string[] = [];\n readyState = FakeSocket.CONNECTING;\n readonly #listeners = new Map<string, Set<Listener>>();\n #session: ReturnType<FakeRelayCore[\"connect\"]> | undefined;\n #queuedSends: string[] = [];\n readonly #core: FakeRelayCore;\n\n constructor(url: string) {\n this.url = url;\n this.#core = coreFor(url);\n queueMicrotask(() => this.#open());\n }\n\n #open(): void {\n if (this.readyState !== FakeSocket.CONNECTING) {\n return;\n }\n this.readyState = FakeSocket.OPEN;\n const transport: RelayTransport = {\n send: (data) => this.#emit(\"message\", { data }),\n close: () => this.#terminate(),\n };\n // \"open\" listeners may send immediately; relay frames (e.g. the AUTH\n // challenge) must follow \"open\", so connect afterwards and flush.\n this.#emit(\"open\", {});\n this.#session = this.#core.connect(transport);\n const queued = this.#queuedSends;\n this.#queuedSends = [];\n for (const data of queued) {\n this.#core.handleMessage(this.#session, data);\n }\n }\n\n send(data: string): void {\n // Browser semantics: CONNECTING throws InvalidStateError; CLOSING/CLOSED\n // silently discard.\n if (this.readyState === FakeSocket.CONNECTING) {\n throw new Error(\"InvalidStateError: WebSocket is not open: readyState 0 (CONNECTING)\");\n }\n if (this.readyState !== FakeSocket.OPEN) {\n return;\n }\n this.sent.push(data);\n if (this.#session) {\n this.#core.handleMessage(this.#session, data);\n } else {\n this.#queuedSends.push(data);\n }\n }\n\n close(): void {\n this.#terminate();\n }\n\n #terminate(): void {\n if (this.readyState === FakeSocket.CLOSING || this.readyState === FakeSocket.CLOSED) {\n this.readyState = FakeSocket.CLOSED;\n return;\n }\n this.readyState = FakeSocket.CLOSED;\n const session = this.#session;\n this.#session = undefined;\n if (session) {\n this.#core.detach(session);\n }\n this.#emit(\"close\", {});\n }\n\n addEventListener(type: string, listener: Listener): void {\n let set = this.#listeners.get(type);\n if (!set) {\n set = new Set();\n this.#listeners.set(type, set);\n }\n set.add(listener);\n }\n\n removeEventListener(type: string, listener: Listener): void {\n this.#listeners.get(type)?.delete(listener);\n }\n\n #emit(type: string, ev: unknown): void {\n for (const listener of this.#listeners.get(type) ?? []) {\n listener(ev);\n }\n }\n }\n\n return {\n websocketImplementation: FakeSocket,\n relay: coreFor,\n close() {\n for (const core of cores.values()) {\n core.disconnect();\n }\n cores.clear();\n },\n };\n}\n","import { FakeRelayCore } from \"./relay-core.ts\";\nimport type { FakeRelay, FakeRelayOptions, RelayTransport } from \"./relay-core.ts\";\n\nexport type ServedFakeRelay = {\n url: string;\n relay: FakeRelay;\n close: () => Promise<void>;\n};\n\n/**\n * Serve the fake relay on a real WebSocket port via the optional `ws` peer dependency (dynamic\n * import — `@qntx/nostr/testing` never requires `ws` unless you serve). `port: 0` picks an\n * ephemeral port.\n */\nexport async function serveFakeRelay(\n opts: FakeRelayOptions & { port?: number; host?: string } = {},\n): Promise<ServedFakeRelay> {\n const { port = 0, host = \"127.0.0.1\", ...relayOpts } = opts;\n const { WebSocketServer } = await import(\"ws\");\n const wss = new WebSocketServer({ port, host });\n await new Promise<void>((resolve, reject) => {\n wss.once(\"listening\", () => resolve());\n wss.once(\"error\", (err) => reject(err));\n });\n const address = wss.address();\n const bound = typeof address === \"object\" && address !== null ? address.port : port;\n const url = `ws://${host}:${bound}`;\n const core = new FakeRelayCore(url, relayOpts);\n\n wss.on(\"connection\", (socket) => {\n const transport: RelayTransport = {\n send: (data) => socket.send(data),\n close: () => socket.close(),\n };\n const session = core.connect(transport);\n socket.on(\"message\", (data: unknown) => {\n core.handleMessage(session, String(data));\n });\n socket.on(\"close\", () => core.detach(session));\n socket.on(\"error\", () => core.detach(session));\n });\n\n return {\n url,\n relay: core,\n close: async () =>\n new Promise((resolve) => {\n core.disconnect();\n for (const client of wss.clients) {\n client.terminate();\n }\n wss.close(() => resolve());\n }),\n };\n}\n","import { EventBuilder, Keys } from \"../core/index.ts\";\nimport type { Event } from \"../core/index.ts\";\nimport type { EventStore } from \"../storage/types.ts\";\n\nexport { type Filter } from \"../core/filter.ts\";\n\nexport type EventStoreConformanceCase = {\n name: string;\n run: (store: EventStore) => Promise<void>;\n};\n\nconst ALICE_SK = \"0000000000000000000000000000000000000000000000000000000000000101\";\nconst BOB_SK = \"0000000000000000000000000000000000000000000000000000000000000102\";\n\nfunction alice(): Keys {\n return Keys.fromSecretKey(ALICE_SK);\n}\nfunction bob(): Keys {\n return Keys.fromSecretKey(BOB_SK);\n}\n\nfunction note(keys: Keys, content: string, createdAt: number, tags: string[][] = []): Event {\n return new EventBuilder(1, content).tags(tags).createdAt(createdAt).signWithKeys(keys);\n}\n\nfunction replaceable(keys: Keys, kind: number, content: string, createdAt: number): Event {\n return new EventBuilder(kind, content).createdAt(createdAt).signWithKeys(keys);\n}\n\nfunction addressable(\n keys: Keys,\n kind: number,\n d: string,\n content: string,\n createdAt: number,\n): Event {\n return new EventBuilder(kind, content)\n .tags([[\"d\", d]])\n .createdAt(createdAt)\n .signWithKeys(keys);\n}\n\nfunction fail(message: string): never {\n throw new Error(`EventStore conformance: ${message}`);\n}\n\nfunction eq<T>(got: T, want: T, what: string): void {\n if (JSON.stringify(got) !== JSON.stringify(want)) {\n fail(`${what}: expected ${JSON.stringify(want)}, got ${JSON.stringify(got)}`);\n }\n}\n\nfunction ids(events: ReadonlyArray<Event>): string[] {\n return events.map((e) => e.id);\n}\n\n/**\n * Framework-agnostic {@link EventStore} conformance cases. Each `run` throws on failure; register\n * them with the test framework of your choice: `for (const c of eventStoreConformanceCases)\n * test(c.name, () => c.run(store()))`.\n */\nexport const eventStoreConformanceCases: ReadonlyArray<EventStoreConformanceCase> = [\n {\n name: \"put accepted then duplicate\",\n async run(store) {\n const e = note(alice(), \"hello\", 1);\n eq(await store.put(e), \"accepted\", \"first put\");\n eq(await store.put(e), \"duplicate\", \"second put\");\n eq(await store.get(e.id), e, \"get after put\");\n eq(\n await store.putMany([note(alice(), \"a\", 2), note(alice(), \"b\", 3)]),\n [\"accepted\", \"accepted\"],\n \"putMany results\",\n );\n },\n },\n {\n name: \"ephemeral event returns ephemeral and is not stored\",\n async run(store) {\n const e = replaceable(alice(), 20001, \"ephemeral\", 1);\n eq(await store.put(e), \"ephemeral\", \"put ephemeral\");\n eq(await store.get(e.id), undefined, \"ephemeral not retrievable\");\n eq(await store.query([{ kinds: [20001] }]), [], \"ephemeral not queryable\");\n eq(await store.count([{ kinds: [20001] }]), 0, \"ephemeral not counted\");\n },\n },\n {\n name: \"replaceable event keeps newest and rejects stale\",\n async run(store) {\n const older = replaceable(alice(), 0, \"old\", 1);\n const newer = replaceable(alice(), 0, \"new\", 2);\n eq(await store.put(older), \"accepted\", \"first replaceable\");\n eq(await store.put(newer), \"replaced\", \"newer replaces\");\n eq(await store.put(older), \"rejected\", \"stale replaceable rejected\");\n eq(await store.get(older.id), undefined, \"replaced event gone\");\n eq(\n ids(await store.query([{ kinds: [0], authors: [alice().publicKey] }])),\n [newer.id],\n \"newest kept\",\n );\n },\n },\n {\n name: \"addressable event replaces by address\",\n async run(store) {\n const v1 = addressable(alice(), 30001, \"x\", \"v1\", 1);\n const v2 = addressable(alice(), 30001, \"x\", \"v2\", 2);\n const other = addressable(alice(), 30001, \"y\", \"other\", 3);\n eq(await store.put(v1), \"accepted\", \"first addressable\");\n eq(await store.put(v2), \"replaced\", \"same address replaced\");\n eq(await store.put(v1), \"rejected\", \"stale addressable rejected\");\n eq(await store.put(other), \"accepted\", \"different address independent\");\n eq(await store.get(v1.id), undefined, \"v1 gone\");\n eq(\n ids(await store.query([{ kinds: [30001], authors: [alice().publicKey] }])).sort(),\n [other.id, v2.id].sort(),\n \"independent addresses\",\n );\n },\n },\n {\n name: \"deletion removes same-author e-tag targets only\",\n async run(store) {\n const mine = note(alice(), \"mine\", 1);\n const theirs = note(bob(), \"theirs\", 1);\n await store.put(mine);\n await store.put(theirs);\n const delForeign = EventBuilder.deletion([theirs.id]).createdAt(2).signWithKeys(alice());\n eq(await store.put(delForeign), \"deleted\", \"kind 5 accepted\");\n eq(await store.get(theirs.id), theirs, \"other author's event survives\");\n const delMine = EventBuilder.deletion([mine.id]).createdAt(3).signWithKeys(alice());\n eq(await store.put(delMine), \"deleted\", \"kind 5 accepted\");\n eq(await store.get(mine.id), undefined, \"own event deleted\");\n eq(await store.query([{ ids: [mine.id] }]), [], \"deleted event not queryable\");\n eq(await store.put(mine), \"duplicate\", \"deleted event cannot be re-put\");\n },\n },\n {\n name: \"deletion removes addressable via a tag and tombstones re-put\",\n async run(store) {\n const target = addressable(alice(), 30001, \"z\", \"target\", 1);\n await store.put(target);\n const coord = `30001:${alice().publicKey}:z`;\n const del = EventBuilder.deletion([{ address: coord }], \"\")\n .createdAt(2)\n .signWithKeys(alice());\n eq(await store.put(del), \"deleted\", \"a-tag deletion\");\n eq(await store.get(target.id), undefined, \"addressable deleted\");\n const stillCovered = addressable(alice(), 30001, \"z\", \"covered\", 1);\n eq(await store.put(stillCovered), \"duplicate\", \"tombstoned address rejects re-put\");\n eq(await store.query([{ kinds: [30001], authors: [alice().publicKey] }]), [], \"none left\");\n const after = addressable(alice(), 30001, \"z\", \"after\", 3);\n eq(await store.put(after), \"accepted\", \"events newer than the deletion are kept\");\n },\n },\n {\n name: \"query orders newest first and applies limit\",\n async run(store) {\n const a = note(alice(), \"a\", 1);\n const b = note(alice(), \"b\", 3);\n const c = note(alice(), \"c\", 2);\n await store.putMany([a, b, c]);\n eq(\n ids(await store.query([{ kinds: [1], authors: [alice().publicKey] }])),\n [b.id, c.id, a.id],\n \"descending created_at\",\n );\n eq(\n ids(await store.query([{ kinds: [1], authors: [alice().publicKey], limit: 2 }])),\n [b.id, c.id],\n \"limit applies\",\n );\n },\n },\n {\n name: \"query filters by kinds, authors, #e, #p, since, until\",\n async run(store) {\n const target = note(bob(), \"target\", 1);\n const tagged = note(alice(), \"tagged\", 2, [\n [\"e\", target.id],\n [\"p\", bob().publicKey],\n ]);\n const plain = note(alice(), \"plain\", 3);\n await store.putMany([target, tagged, plain]);\n const all = await store.query([{ kinds: [1] }]);\n eq(all.length, 3, \"kind filter\");\n eq(ids(await store.query([{ authors: [bob().publicKey] }])), [target.id], \"authors\");\n eq(ids(await store.query([{ \"#e\": [target.id] }])), [tagged.id], \"#e\");\n eq(ids(await store.query([{ \"#p\": [bob().publicKey] }])), [tagged.id], \"#p\");\n eq(ids(await store.query([{ kinds: [1], since: 2 }])), [plain.id, tagged.id], \"since\");\n eq(ids(await store.query([{ kinds: [1], until: 2 }])), [tagged.id, target.id], \"until\");\n eq(\n ids(await store.query([{ ids: [target.id, tagged.id] }])).sort(),\n [tagged.id, target.id].sort(),\n \"ids filter\",\n );\n },\n },\n {\n name: \"#e/#p tag values compare verbatim, lowercasing only the filter side\",\n async run(store) {\n const target = note(bob(), \"target\", 1);\n const upper = note(alice(), \"upper\", 2, [\n [\"e\", target.id.toUpperCase()],\n [\"p\", bob().publicKey.toUpperCase()],\n ]);\n const lower = note(alice(), \"lower\", 3, [\n [\"e\", target.id],\n [\"p\", bob().publicKey],\n ]);\n await store.putMany([target, upper, lower]);\n // Filter values are caller input and canonicalize to lowercase; the event's tag value is\n // compared literally, so the uppercase-tagged event never matches.\n eq(ids(await store.query([{ \"#e\": [target.id] }])), [lower.id], \"#e lowercase filter\");\n eq(\n ids(await store.query([{ \"#e\": [target.id.toUpperCase()] }])),\n [lower.id],\n \"#e uppercase filter\",\n );\n eq(\n ids(await store.query([{ \"#p\": [bob().publicKey.toUpperCase()] }])),\n [lower.id],\n \"#p uppercase filter\",\n );\n },\n },\n {\n name: \"multi-letter # keys are ignored (NIP-01 single-letter tag conditions only)\",\n async run(store) {\n const tagged = note(alice(), \"tagged\", 1, [[\"client\", \"test-app\"]]);\n const plain = note(bob(), \"plain\", 2);\n await store.putMany([tagged, plain]);\n eq(await store.count([{ \"#client\": [\"test-app\"] }]), 2, \"ignored on count\");\n eq(await store.count([{ \"#client\": [\"other\"] }]), 2, \"ignored regardless of value\");\n eq(\n ids(await store.query([{ \"#client\": [\"test-app\"] }])).sort(),\n [plain.id, tagged.id].sort(),\n \"ignored on query\",\n );\n },\n },\n {\n name: \"count matches query cardinality across overlapping filters\",\n async run(store) {\n const a = note(alice(), \"a\", 1);\n const b = note(bob(), \"b\", 2);\n await store.putMany([a, b]);\n eq(await store.count([{ kinds: [1] }]), 2, \"count all\");\n eq(\n await store.count([\n { authors: [alice().publicKey] },\n { authors: [bob().publicKey] },\n { ids: [a.id] },\n ]),\n 2,\n \"count dedupes union\",\n );\n eq(await store.count([{ kinds: [2] }]), 0, \"count none\");\n },\n },\n {\n name: \"negentropyItems sorted by created_at then id and respects limit\",\n async run(store) {\n const a = note(alice(), \"a\", 2);\n const b = note(alice(), \"b\", 1);\n const c = note(bob(), \"c\", 3);\n await store.putMany([a, b, c]);\n const items = await store.negentropyItems({ kinds: [1] });\n eq(\n items.map((i) => i.id),\n [b.id, a.id, c.id],\n \"created_at asc ordering\",\n );\n const limited = await store.negentropyItems({ kinds: [1], limit: 2 });\n eq(\n limited.map((i) => i.id),\n [a.id, c.id],\n \"limit keeps newest two, presented ascending\",\n );\n eq(await store.negentropyItems({ kinds: [2] }), [], \"no match\");\n const filtered = await store.negentropyItems({ authors: [bob().publicKey] });\n eq(filtered, [{ id: c.id, created_at: 3 }], \"filter\");\n },\n },\n {\n name: \"outbox bounds roundtrip\",\n async run(store) {\n eq(await store.getOutboxBound(alice().publicKey, 1), undefined, \"unset bound\");\n await store.setOutboxBound(alice().publicKey, 1, { oldest: 10, newest: 20 });\n eq(\n await store.getOutboxBound(alice().publicKey, 1),\n { oldest: 10, newest: 20 },\n \"bound read\",\n );\n await store.setOutboxBound(alice().publicKey, 1, { oldest: 5, newest: 30 });\n eq(\n await store.getOutboxBound(alice().publicKey, 1),\n { oldest: 5, newest: 30 },\n \"bound overwrite\",\n );\n },\n },\n {\n name: \"remove deletes by id and tombstones\",\n async run(store) {\n const a = note(alice(), \"a\", 1);\n await store.put(a);\n eq(await store.remove([a.id]), 1, \"remove count\");\n eq(await store.get(a.id), undefined, \"removed\");\n eq(await store.remove([a.id]), 0, \"second remove is noop\");\n eq(await store.put(a), \"duplicate\", \"removed id tombstoned\");\n },\n },\n {\n name: \"clear wipes events and bounds\",\n async run(store) {\n const a = note(alice(), \"a\", 1);\n await store.put(a);\n await store.setOutboxBound(alice().publicKey, 1, { oldest: 1, newest: 1 });\n await store.clear();\n eq(await store.get(a.id), undefined, \"event gone\");\n eq(await store.query([{ kinds: [1] }]), [], \"query empty\");\n eq(await store.getOutboxBound(alice().publicKey, 1), undefined, \"bound gone\");\n eq(await store.put(a), \"accepted\", \"store usable after clear\");\n },\n },\n];\n","import { Keys, finalizeEvent } from \"../core/index.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { isTag } from \"../core/tag.ts\";\nimport { isRecord, nowSeconds } from \"../core/util.ts\";\nimport {\n getConversationKey,\n decrypt as nip44Decrypt,\n encrypt as nip44Encrypt,\n} from \"../nips/nip44.ts\";\nimport { decodeNip46Request, encodeNip46Response } from \"../nips/nip46.ts\";\nimport type { Nip46Response } from \"../nips/nip46.ts\";\nimport type { FakeRelayNetwork } from \"./network.ts\";\n\nexport type FakeNip46SignerOptions = {\n /** The network the fake signer joins as a client of `relayUrl`. */\n network: FakeRelayNetwork;\n relayUrl: string;\n /** Pubkey of the NIP-46 client under test. */\n clientPubkey: string;\n /** Remote-signer key; random when omitted. */\n bunkerSk?: string | undefined;\n /** The user key the signer signs for; random when omitted. */\n userSk?: string | undefined;\n /** First answer `auth_url` (result) with this URL in the error field, then the real response. */\n authUrl?: string | undefined;\n authUrlMethods?: ReadonlyArray<string> | undefined;\n /** Delay in ms before the real response after an `auth_url` reply. */\n authReplyDelayMs?: number | undefined;\n /** Collected RPC requests (mutated as they arrive). */\n requests?: Array<{ method: string; params: ReadonlyArray<string> }> | undefined;\n /** `switch_relays` result. Default `\"null\"`. */\n switchRelays?: string[] | undefined;\n /** Override `connect` RPC result. Default `\"ack\"`. */\n connectResult?: string | undefined;\n /** Reply with neither `result` nor `error` for these methods. */\n emptyMethods?: ReadonlyArray<string> | undefined;\n /**\n * Serialize the response JSON; default {@link encodeNip46Response}, which omits absent fields.\n * Lets tests put explicit `null`s on the wire like some remote signers do.\n */\n encodeResponse?: ((res: Nip46Response) => string) | undefined;\n};\n\nexport type FakeNip46Signer = {\n readonly bunkerPubkey: string;\n readonly userPublicKey: string;\n /** Also answer RPCs on another relay of the same network (e.g. after switch_relays). */\n attach: (relayUrl: string) => void;\n /** Publish the nostrconnect handshake secret confirmation as the bunker. */\n confirmHandshake: (secret: string) => void;\n close: () => void;\n};\n\n/**\n * In-process NIP-46 remote signer: subscribes to kind:24133 requests on `relayUrl` and answers as a\n * bunker, encrypted to `clientPubkey` with NIP-44.\n */\nexport function createFakeNip46Signer(opts: FakeNip46SignerOptions): FakeNip46Signer {\n const bunkerKeys =\n opts.bunkerSk !== undefined && opts.bunkerSk !== \"\"\n ? Keys.fromSecretKey(opts.bunkerSk)\n : Keys.generate();\n const userKeys =\n opts.userSk !== undefined && opts.userSk !== \"\"\n ? Keys.fromSecretKey(opts.userSk)\n : Keys.generate();\n const convKey = getConversationKey(bunkerKeys.secretKey.bytes, opts.clientPubkey);\n const handled = new Set<string>();\n const authPending = new Set<string>();\n const authMethods = new Set(opts.authUrlMethods ?? [\"connect\"]);\n let closed = false;\n\n const sockets = new Set<{ close: () => void }>();\n const listen = (relayUrl: string): void => {\n const ws = new opts.network.websocketImplementation(relayUrl);\n ws.addEventListener(\"open\", () => {\n ws.send(\n JSON.stringify([\n \"REQ\",\n \"fake-nip46-signer\",\n { kinds: [Kind.NostrConnect], \"#p\": [bunkerKeys.publicKey] },\n ]),\n );\n });\n ws.addEventListener(\"message\", (ev) => onMessage(ev, ws));\n sockets.add(ws);\n };\n\n const onMessage = (ev: unknown, ws: { send: (data: string) => void }): void => {\n if (typeof ev !== \"object\" || ev === null || !(\"data\" in ev)) {\n return;\n }\n const { data } = ev;\n let msg: unknown;\n try {\n msg = JSON.parse(String(data));\n } catch {\n return;\n }\n if (!Array.isArray(msg) || msg[0] !== \"EVENT\") {\n return;\n }\n const parts: unknown[] = msg;\n const rawEvent = parts.at(2);\n if (!isRecord(rawEvent)) {\n return;\n }\n const { kind, pubkey, content, id } = rawEvent;\n if (kind !== Kind.NostrConnect) {\n return;\n }\n if (typeof pubkey !== \"string\" || typeof content !== \"string\" || typeof id !== \"string\") {\n return;\n }\n if (pubkey !== opts.clientPubkey || handled.has(id)) {\n return;\n }\n\n try {\n const req = decodeNip46Request(nip44Decrypt(content, convKey));\n if (\n opts.authUrl !== undefined &&\n opts.authUrl !== \"\" &&\n authMethods.has(req.method) &&\n !authPending.has(req.id)\n ) {\n authPending.add(req.id);\n reply(ws, opts.clientPubkey, req.id, \"auth_url\", opts.authUrl);\n }\n handled.add(id);\n opts.requests?.push({ method: req.method, params: req.params });\n\n let result: string | undefined;\n let error: string | undefined;\n switch (opts.emptyMethods?.includes(req.method) === true ? \"#empty\" : req.method) {\n case \"connect\":\n result = opts.connectResult ?? \"ack\";\n break;\n case \"get_public_key\":\n result = userKeys.publicKey;\n break;\n case \"ping\":\n result = \"pong\";\n break;\n case \"switch_relays\":\n result = opts.switchRelays === undefined ? \"null\" : JSON.stringify(opts.switchRelays);\n break;\n case \"logout\":\n result = \"ack\";\n break;\n case \"sign_event\": {\n const rawTemplate: unknown = JSON.parse(req.params.at(0) ?? \"null\");\n if (\n !isRecord(rawTemplate) ||\n typeof rawTemplate[\"kind\"] !== \"number\" ||\n !Array.isArray(rawTemplate[\"tags\"]) ||\n !rawTemplate[\"tags\"].every(isTag) ||\n typeof rawTemplate[\"content\"] !== \"string\" ||\n typeof rawTemplate[\"created_at\"] !== \"number\"\n ) {\n throw new Error(\"invalid sign_event template\");\n }\n result = JSON.stringify(\n finalizeEvent(\n {\n kind: rawTemplate[\"kind\"],\n tags: rawTemplate[\"tags\"],\n content: rawTemplate[\"content\"],\n created_at: rawTemplate[\"created_at\"],\n },\n userKeys.secretKey,\n ),\n );\n break;\n }\n case \"#empty\":\n break;\n default:\n error = `unsupported method ${req.method}`;\n }\n if (authPending.has(req.id) && opts.authReplyDelayMs !== undefined) {\n const pendingId = req.id;\n const pendingResult = result;\n const pendingError = error;\n setTimeout(\n () => reply(ws, opts.clientPubkey, pendingId, pendingResult, pendingError),\n opts.authReplyDelayMs,\n );\n } else {\n reply(ws, opts.clientPubkey, req.id, result, error);\n }\n } catch {\n handled.add(id);\n }\n };\n\n const reply = (\n ws: { send: (data: string) => void },\n clientPubkey: string,\n id: string,\n result?: string,\n error?: string,\n ): void => {\n if (closed) {\n return;\n }\n const payload = (opts.encodeResponse ?? encodeNip46Response)({ id, result, error });\n const event = finalizeEvent(\n {\n kind: Kind.NostrConnect,\n tags: [[\"p\", clientPubkey]],\n content: nip44Encrypt(payload, convKey),\n created_at: nowSeconds(),\n },\n bunkerKeys.secretKey,\n );\n ws.send(JSON.stringify([\"EVENT\", event]));\n };\n\n listen(opts.relayUrl);\n\n return {\n bunkerPubkey: bunkerKeys.publicKey,\n userPublicKey: userKeys.publicKey,\n confirmHandshake(secret: string): void {\n const payload = encodeNip46Response({ id: \"handshake\", result: secret });\n const event = finalizeEvent(\n {\n kind: Kind.NostrConnect,\n tags: [[\"p\", opts.clientPubkey]],\n content: nip44Encrypt(payload, convKey),\n created_at: nowSeconds(),\n },\n bunkerKeys.secretKey,\n );\n opts.network.relay(opts.relayUrl).inject(event);\n },\n attach(relayUrl: string): void {\n listen(relayUrl);\n },\n close(): void {\n closed = true;\n for (const ws of sockets) {\n ws.close();\n }\n sockets.clear();\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as Event } from "./event-j3BwI5dF.mjs";
|
|
2
|
-
import { t as Filter } from "./filter-
|
|
2
|
+
import { t as Filter } from "./filter-oYG2zjJ3.mjs";
|
|
3
3
|
//#region src/storage/types.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* `"invalid"`: the event failed canonical shape validation (see `validateSignedEvent`); it was not
|
|
@@ -44,4 +44,4 @@ type EventStore = {
|
|
|
44
44
|
};
|
|
45
45
|
//#endregion
|
|
46
46
|
export { PutResult as i, NegentropyItem as n, OutboxBound as r, EventStore as t };
|
|
47
|
-
//# sourceMappingURL=types-
|
|
47
|
+
//# sourceMappingURL=types-DiVVyf1e.d.mts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
|
|
1
|
+
import { bytesToHex, hexToBytes, randomBytes } from "@noble/hashes/utils.js";
|
|
2
2
|
//#region src/core/limits.ts
|
|
3
3
|
/** NIP-01 event id length in bytes. */
|
|
4
4
|
const EVENT_ID_BYTES = 32;
|
|
@@ -44,10 +44,6 @@ var CryptoError = class extends NostrError {
|
|
|
44
44
|
var MessageError = class extends NostrError {
|
|
45
45
|
name = "MessageError";
|
|
46
46
|
};
|
|
47
|
-
/** Wasm instance aborted; later calls on that instance fail. */
|
|
48
|
-
var WasmPoisonedError = class extends NostrError {
|
|
49
|
-
name = "WasmPoisonedError";
|
|
50
|
-
};
|
|
51
47
|
//#endregion
|
|
52
48
|
//#region src/core/util.ts
|
|
53
49
|
const utf8Encoder = new TextEncoder();
|
|
@@ -58,6 +54,10 @@ const HEX64_RE = /^[0-9a-f]{128}$/;
|
|
|
58
54
|
function bytesToHex$1(bytes) {
|
|
59
55
|
return bytesToHex(bytes);
|
|
60
56
|
}
|
|
57
|
+
/** Cryptographically secure random bytes (Web Crypto under the hood). */
|
|
58
|
+
function randomBytes$1(length) {
|
|
59
|
+
return randomBytes(length);
|
|
60
|
+
}
|
|
61
61
|
/** Decode lowercase or mixed-case hex to bytes. */
|
|
62
62
|
function hexToBytes$1(hex) {
|
|
63
63
|
try {
|
|
@@ -100,13 +100,33 @@ function isRecord(value) {
|
|
|
100
100
|
* Char-by-char because Hermes may lack `String.prototype.isWellFormed`.
|
|
101
101
|
*/
|
|
102
102
|
function hasLoneSurrogate(value) {
|
|
103
|
-
for (
|
|
104
|
-
const code =
|
|
105
|
-
if (code
|
|
103
|
+
for (let i = 0; i < value.length; i++) {
|
|
104
|
+
const code = value.charCodeAt(i);
|
|
105
|
+
if (code < 55296 || code > 57343) continue;
|
|
106
|
+
if (code <= 56319) {
|
|
107
|
+
const next = value.charCodeAt(i + 1);
|
|
108
|
+
if (next >= 56320 && next <= 57343) {
|
|
109
|
+
i += 1;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
106
114
|
}
|
|
107
115
|
return false;
|
|
108
116
|
}
|
|
109
117
|
/**
|
|
118
|
+
* Deep form of {@link hasLoneSurrogate}: true when any string in `value` — including record keys —
|
|
119
|
+
* carries an unpaired surrogate. `JSON.parse` accepts `\ud800`-style escapes that serde_json
|
|
120
|
+
* rejects, so JSON codecs validate the parsed value with this before the shape checks to keep both
|
|
121
|
+
* languages agreeing on the invalid-JSON error (N10).
|
|
122
|
+
*/
|
|
123
|
+
function containsLoneSurrogate(value) {
|
|
124
|
+
if (typeof value === "string") return hasLoneSurrogate(value);
|
|
125
|
+
if (Array.isArray(value)) return value.some(containsLoneSurrogate);
|
|
126
|
+
if (isRecord(value)) return Object.entries(value).some(([key, entry]) => hasLoneSurrogate(key) || containsLoneSurrogate(entry));
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
110
130
|
* Normalize a relay URL to a stable form: `http:`/`https:` are rewritten to `ws:`/`wss:` (a bare
|
|
111
131
|
* host gets `wss://`), any other scheme throws UrlError. The result has a lowercased host, the
|
|
112
132
|
* default port removed, duplicate path slashes collapsed, a sorted query, and no fragment. `URL`
|
|
@@ -146,6 +166,6 @@ function normalizeRelayUrls(urls) {
|
|
|
146
166
|
return [...seen];
|
|
147
167
|
}
|
|
148
168
|
//#endregion
|
|
149
|
-
export {
|
|
169
|
+
export { errorMessage as C, SECRET_KEY_BYTES as D, PUBLIC_KEY_BYTES as E, SIGNATURE_BYTES as O, UrlError as S, EVENT_ID_BYTES as T, CryptoError as _, containsLoneSurrogate as a, MessageError as b, isHex32 as c, normalizeRelayUrls as d, normalizeURL as f, utf8Encoder as g, utf8Decoder as h, bytesToHex$1 as i, SUBSCRIPTION_ID_MAX_CHARS as k, isHex64 as l, randomBytes$1 as m, assertHex32 as n, hasLoneSurrogate as o, nowSeconds as p, assertSecretKeyBytes as r, hexToBytes$1 as s, assertByteLength as t, isRecord as u, EventValidationError as v, toError as w, NostrError as x, HexError as y };
|
|
150
170
|
|
|
151
|
-
//# sourceMappingURL=util-
|
|
171
|
+
//# sourceMappingURL=util-DfsyJ9nz.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util-DfsyJ9nz.mjs","names":["bytesToHex","nobleBytesToHex","randomBytes","nobleRandomBytes","hexToBytes","nobleHexToBytes"],"sources":["../src/core/limits.ts","../src/core/error.ts","../src/core/util.ts"],"sourcesContent":["/** NIP-01 event id length in bytes. */\nexport const EVENT_ID_BYTES = 32;\n\n/** BIP-340 signature length in bytes. */\nexport const SIGNATURE_BYTES = 64;\n\n/** BIP-340 public key length in bytes (x-only). */\nexport const PUBLIC_KEY_BYTES = 32;\n\n/** BIP-340 secret key length in bytes. */\nexport const SECRET_KEY_BYTES = 32;\n\n/** NIP-01 subscription id max character length. */\nexport const SUBSCRIPTION_ID_MAX_CHARS = 64;\n","/** Base error for all @qntx/nostr failures. */\nexport class NostrError extends Error {\n override name = \"NostrError\";\n}\n\n/** `error.message` for Error instances, `String(error)` otherwise. */\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** The Error itself, or a NostrError carrying `String(value)` for non-Error throws. */\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new NostrError(errorMessage(value));\n}\n\n/** Invalid hex encoding or length. */\nexport class HexError extends NostrError {\n override name = \"HexError\";\n}\n\n/** Invalid URL / relay URL. */\nexport class UrlError extends NostrError {\n override name = \"UrlError\";\n}\n\n/** Event shape / wire validation failure. */\nexport class EventValidationError extends NostrError {\n override name = \"EventValidationError\";\n}\n\n/** Cryptographic operation failure (keys, signatures). */\nexport class CryptoError extends NostrError {\n override name = \"CryptoError\";\n}\n\n/** Message parse / encode failure. */\nexport class MessageError extends NostrError {\n override name = \"MessageError\";\n}\n","import {\n bytesToHex as nobleBytesToHex,\n hexToBytes as nobleHexToBytes,\n randomBytes as nobleRandomBytes,\n} from \"@noble/hashes/utils.js\";\n\nimport { HexError, UrlError } from \"./error.ts\";\nimport { SECRET_KEY_BYTES } from \"./limits.ts\";\n\nexport const utf8Encoder: TextEncoder = new TextEncoder();\nexport const utf8Decoder: TextDecoder = new TextDecoder();\n\nconst HEX32_RE = /^[0-9a-f]{64}$/;\nconst HEX64_RE = /^[0-9a-f]{128}$/;\n\n/** Lowercase hex encode. */\nexport function bytesToHex(bytes: Uint8Array): string {\n return nobleBytesToHex(bytes);\n}\n\n/** Cryptographically secure random bytes (Web Crypto under the hood). */\nexport function randomBytes(length: number): Uint8Array {\n return nobleRandomBytes(length);\n}\n\n/** Decode lowercase or mixed-case hex to bytes. */\nexport function hexToBytes(hex: string): Uint8Array {\n try {\n return nobleHexToBytes(hex);\n } catch (error) {\n throw new HexError(`invalid hex string of length ${hex.length}`, {\n cause: error,\n });\n }\n}\n\n/** True when value is canonical NIP-01 lowercase hex of 32 bytes (64 chars). */\nexport function isHex32(value: string): boolean {\n return HEX32_RE.test(value);\n}\n\n/** True when value is canonical NIP-01 lowercase hex of 64 bytes (128 chars). */\nexport function isHex64(value: string): boolean {\n return HEX64_RE.test(value);\n}\n\n/** Caller input of any case: lowercases first, then requires canonical hex shape. */\nexport function assertHex32(value: string, label: string): string {\n const normalized = value.toLowerCase();\n if (!isHex32(normalized)) {\n throw new HexError(`invalid ${label}: expected 64-char hex`);\n }\n return normalized;\n}\n\nexport function assertByteLength(bytes: Uint8Array, expected: number, label: string): void {\n if (bytes.length !== expected) {\n throw new HexError(`invalid ${label} length: expected ${expected}, got ${bytes.length}`);\n }\n}\n\nexport function assertSecretKeyBytes(bytes: Uint8Array): void {\n assertByteLength(bytes, SECRET_KEY_BYTES, \"secret key\");\n}\n\n/** Current unix time in whole seconds (NIP-01 `created_at`). */\nexport function nowSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\n/** Structural guard: a plain record (not `null`, not an array). */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * True when `value` contains a UTF-16 surrogate code unit without its other half (NIP-01 events\n * reject these; `JSON.parse` accepts `\\ud800`-style escapes, so validate after parsing).\n * Char-by-char because Hermes may lack `String.prototype.isWellFormed`.\n */\nexport function hasLoneSurrogate(value: string): boolean {\n // Code-unit scan: a high surrogate must be followed by a low surrogate; a\n // lone low surrogate fails on its own.\n for (let i = 0; i < value.length; i++) {\n // oxlint-disable-next-line unicorn/prefer-code-point -- surrogate halves need code units, not code points\n const code = value.charCodeAt(i);\n if (code < 0xd800 || code > 0xdfff) {\n continue;\n }\n if (code <= 0xdbff) {\n // oxlint-disable-next-line unicorn/prefer-code-point -- surrogate halves need code units, not code points\n const next = value.charCodeAt(i + 1);\n if (next >= 0xdc00 && next <= 0xdfff) {\n i += 1;\n continue;\n }\n }\n return true;\n }\n return false;\n}\n\n/**\n * Deep form of {@link hasLoneSurrogate}: true when any string in `value` — including record keys —\n * carries an unpaired surrogate. `JSON.parse` accepts `\\ud800`-style escapes that serde_json\n * rejects, so JSON codecs validate the parsed value with this before the shape checks to keep both\n * languages agreeing on the invalid-JSON error (N10).\n */\nexport function containsLoneSurrogate(value: unknown): boolean {\n if (typeof value === \"string\") {\n return hasLoneSurrogate(value);\n }\n if (Array.isArray(value)) {\n return value.some(containsLoneSurrogate);\n }\n if (isRecord(value)) {\n return Object.entries(value).some(\n ([key, entry]) => hasLoneSurrogate(key) || containsLoneSurrogate(entry),\n );\n }\n return false;\n}\n\n/** Mutable view of a readonly exported type, for local construction. */\nexport type Mutable<T> = { -readonly [K in keyof T]: T[K] };\n\n/**\n * Normalize a relay URL to a stable form: `http:`/`https:` are rewritten to `ws:`/`wss:` (a bare\n * host gets `wss://`), any other scheme throws UrlError. The result has a lowercased host, the\n * default port removed, duplicate path slashes collapsed, a sorted query, and no fragment. `URL`\n * serialization keeps a trailing `/` on the root path (`wss://a.example/`).\n */\nexport function normalizeURL(url: string): string {\n try {\n let input = url;\n if (!input.includes(\"://\")) {\n input = `wss://${input}`;\n }\n const p = new URL(input);\n if (p.protocol === \"http:\") {\n p.protocol = \"ws:\";\n } else if (p.protocol === \"https:\") {\n p.protocol = \"wss:\";\n }\n if (p.protocol !== \"ws:\" && p.protocol !== \"wss:\") {\n throw new UrlError(`unsupported relay URL scheme: ${p.protocol}`);\n }\n p.pathname = p.pathname.replaceAll(/\\/+/g, \"/\");\n if (p.pathname.endsWith(\"/\") && p.pathname.length > 1) {\n p.pathname = p.pathname.slice(0, -1);\n }\n if ((p.port === \"80\" && p.protocol === \"ws:\") || (p.port === \"443\" && p.protocol === \"wss:\")) {\n p.port = \"\";\n }\n p.searchParams.sort();\n p.hash = \"\";\n return p.toString();\n } catch (error) {\n if (error instanceof UrlError) {\n throw error;\n }\n throw new UrlError(`invalid URL: ${url}`, {\n cause: error,\n });\n }\n}\n\n/**\n * Normalize each entry like {@link normalizeURL}; empty or invalid entries are skipped and the\n * results are deduplicated in first-seen order.\n */\nexport function normalizeRelayUrls(urls: Iterable<string>): string[] {\n const seen = new Set<string>();\n for (const url of urls) {\n if (url === \"\") {\n continue;\n }\n try {\n seen.add(normalizeURL(url));\n } catch {\n // invalid relay urls are skipped\n }\n }\n return [...seen];\n}\n"],"mappings":"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"}
|