@qntx/nostr 0.10.0 → 0.11.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.
Files changed (99) hide show
  1. package/dist/{builder-CVRmUeNm.mjs → builder-BQbFDO6S.mjs} +22 -5
  2. package/dist/builder-BQbFDO6S.mjs.map +1 -0
  3. package/dist/{client-CeyDLoyO.mjs → client-CTmp5w84.mjs} +14 -14
  4. package/dist/{client-CeyDLoyO.mjs.map → client-CTmp5w84.mjs.map} +1 -1
  5. package/dist/client.d.mts +1 -1
  6. package/dist/client.mjs +1 -1
  7. package/dist/core.d.mts +2 -2
  8. package/dist/core.mjs +7 -7
  9. package/dist/{error-BILMe3Gr.mjs → error-DH4guike.mjs} +2 -2
  10. package/dist/{error-BILMe3Gr.mjs.map → error-DH4guike.mjs.map} +1 -1
  11. package/dist/{error-DlhGW4mJ.mjs → error-UHkqzWL7.mjs} +2 -2
  12. package/dist/{error-DlhGW4mJ.mjs.map → error-UHkqzWL7.mjs.map} +1 -1
  13. package/dist/{event-C2eLiPpV.mjs → event-B3ua2_oL.mjs} +24 -11
  14. package/dist/event-B3ua2_oL.mjs.map +1 -0
  15. package/dist/{filter-TrF2LEoy.mjs → filter-B16P33dz.mjs} +31 -10
  16. package/dist/filter-B16P33dz.mjs.map +1 -0
  17. package/dist/{filter-CfEgQBXt.d.mts → filter-PG7r79rt.d.mts} +6 -2
  18. package/dist/{gossip-BWb9HmfK.d.mts → gossip-BTMdlUI7.d.mts} +2 -2
  19. package/dist/{gossip-5ezg0Quo.mjs → gossip-K_JIexQs.mjs} +3 -3
  20. package/dist/{gossip-5ezg0Quo.mjs.map → gossip-K_JIexQs.mjs.map} +1 -1
  21. package/dist/gossip.d.mts +1 -1
  22. package/dist/gossip.mjs +1 -1
  23. package/dist/{index-B1fLejg4.d.mts → index-BP94lABY.d.mts} +5 -5
  24. package/dist/{index-uCYwXaaP.d.mts → index-CSsnE32i.d.mts} +7 -7
  25. package/dist/{index-CLJEKFGd.d.mts → index-DD0Ywpay.d.mts} +3 -3
  26. package/dist/{index-C0mFpelb.d.mts → index-N7HUGgwo.d.mts} +2 -2
  27. package/dist/{index-CoX7SnO7.d.mts → index-rKcmI6ex.d.mts} +3 -3
  28. package/dist/index.d.mts +11 -11
  29. package/dist/index.mjs +18 -18
  30. package/dist/{key-DY_HEIpY.mjs → key-k8LDEPZh.mjs} +3 -3
  31. package/dist/{key-DY_HEIpY.mjs.map → key-k8LDEPZh.mjs.map} +1 -1
  32. package/dist/{loaders-DifimjJ7.mjs → loaders-Dp2nXEFk.mjs} +6 -6
  33. package/dist/{loaders-DifimjJ7.mjs.map → loaders-Dp2nXEFk.mjs.map} +1 -1
  34. package/dist/loaders.d.mts +2 -2
  35. package/dist/loaders.mjs +1 -1
  36. package/dist/{memory-DENGQ_f6.mjs → memory-VyPnOBU3.mjs} +2 -2
  37. package/dist/{memory-DENGQ_f6.mjs.map → memory-VyPnOBU3.mjs.map} +1 -1
  38. package/dist/{memory-index-D_lvlYAy.mjs → memory-index-Bhwe0ZdG.mjs} +4 -4
  39. package/dist/{memory-index-D_lvlYAy.mjs.map → memory-index-Bhwe0ZdG.mjs.map} +1 -1
  40. package/dist/{message-Rd5Z_17h.mjs → message-pYfVKxPn.mjs} +74 -29
  41. package/dist/message-pYfVKxPn.mjs.map +1 -0
  42. package/dist/nips/blossom.mjs +2 -2
  43. package/dist/nips/nip04.mjs +1 -1
  44. package/dist/nips/nip05.mjs +1 -1
  45. package/dist/nips/nip10.mjs +3 -3
  46. package/dist/nips/nip11.mjs +1 -1
  47. package/dist/nips/nip13.mjs +1 -1
  48. package/dist/nips/nip17.mjs +3 -3
  49. package/dist/nips/nip19.mjs +11 -4
  50. package/dist/nips/nip19.mjs.map +1 -1
  51. package/dist/nips/nip21.mjs +1 -1
  52. package/dist/nips/nip42.mjs +1 -1
  53. package/dist/nips/nip44.mjs +1 -1
  54. package/dist/nips/nip46.mjs +1 -1
  55. package/dist/nips/nip49.mjs +1 -1
  56. package/dist/nips/nip51.mjs +3 -3
  57. package/dist/nips/nip57.mjs +3 -3
  58. package/dist/nips/nip59.mjs +4 -4
  59. package/dist/nips/nip65.mjs +2 -2
  60. package/dist/nips/nip77.mjs +1 -1
  61. package/dist/nips/nip98.mjs +3 -3
  62. package/dist/{pool-jok2-iBJ.mjs → pool-BMUsw2un.mjs} +5 -5
  63. package/dist/{pool-jok2-iBJ.mjs.map → pool-BMUsw2un.mjs.map} +1 -1
  64. package/dist/{pool-Bfe3Tu40.d.mts → pool-DUuYJ0x4.d.mts} +3 -3
  65. package/dist/{put-JsiCKMyZ.mjs → put-DRmV1wHD.mjs} +3 -3
  66. package/dist/{put-JsiCKMyZ.mjs.map → put-DRmV1wHD.mjs.map} +1 -1
  67. package/dist/{reactive-6Wn9qf_d.mjs → reactive--pDpLt2H.mjs} +5 -5
  68. package/dist/{reactive-6Wn9qf_d.mjs.map → reactive--pDpLt2H.mjs.map} +1 -1
  69. package/dist/{reactive-X4p6nABy.d.mts → reactive-jyKrA9ej.d.mts} +3 -3
  70. package/dist/{relay-__QtO33h.mjs → relay-DXtwUEdX.mjs} +6 -6
  71. package/dist/{relay-__QtO33h.mjs.map → relay-DXtwUEdX.mjs.map} +1 -1
  72. package/dist/relay.d.mts +3 -3
  73. package/dist/relay.mjs +2 -2
  74. package/dist/{signer-D7PtVvQn.mjs → signer-CWtxpOxc.mjs} +5 -5
  75. package/dist/{signer-D7PtVvQn.mjs.map → signer-CWtxpOxc.mjs.map} +1 -1
  76. package/dist/signer.d.mts +1 -1
  77. package/dist/signer.mjs +2 -2
  78. package/dist/{storage-oihjE0_x.mjs → storage-B5Wdt_Mn.mjs} +12 -29
  79. package/dist/storage-B5Wdt_Mn.mjs.map +1 -0
  80. package/dist/storage.d.mts +2 -2
  81. package/dist/storage.mjs +4 -4
  82. package/dist/store.d.mts +1 -1
  83. package/dist/store.mjs +1 -1
  84. package/dist/testing.d.mts +1 -1
  85. package/dist/testing.mjs +18 -7
  86. package/dist/testing.mjs.map +1 -1
  87. package/dist/{types-Ccox7-5v.d.mts → types-CqhnT-Mx.d.mts} +2 -2
  88. package/dist/{util-DMF9AIO0.mjs → util-DDpPkiED.mjs} +14 -2
  89. package/dist/util-DDpPkiED.mjs.map +1 -0
  90. package/dist/{verifier-uC_jd-Nw.mjs → verifier-CjH1vnP2.mjs} +3 -3
  91. package/dist/{verifier-uC_jd-Nw.mjs.map → verifier-CjH1vnP2.mjs.map} +1 -1
  92. package/dist/{verifier-9c-bINrv.d.mts → verifier-Cl24c9ph.d.mts} +3 -3
  93. package/package.json +1 -1
  94. package/dist/builder-CVRmUeNm.mjs.map +0 -1
  95. package/dist/event-C2eLiPpV.mjs.map +0 -1
  96. package/dist/filter-TrF2LEoy.mjs.map +0 -1
  97. package/dist/message-Rd5Z_17h.mjs.map +0 -1
  98. package/dist/storage-oihjE0_x.mjs.map +0 -1
  99. package/dist/util-DMF9AIO0.mjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import { t as Event } from "./event-j3BwI5dF.mjs";
2
2
  import { t as WebSocketConstructor } from "./websocket-CmeqvAkp.mjs";
3
- import { t as EventStore } from "./types-Ccox7-5v.mjs";
3
+ import { t as EventStore } from "./types-CqhnT-Mx.mjs";
4
4
  import { a as Nip46Response } from "./nip46-BHaxC9TZ.mjs";
5
5
  //#region src/testing/relay-core.d.ts
6
6
  type FakeRelayOptions = {
package/dist/testing.mjs CHANGED
@@ -1,14 +1,14 @@
1
- import { c as isRecord, d as nowSeconds, i as bytesToHex, u as normalizeURL, x as errorMessage } from "./util-DMF9AIO0.mjs";
1
+ import { S as errorMessage, d as normalizeURL, f as nowSeconds, i as bytesToHex, l as isRecord } from "./util-DDpPkiED.mjs";
2
2
  import { t as Kind } from "./kind-D8fENiiL.mjs";
3
- import { h as validateSignedEvent, t as compareEventsDesc, x as isTag } from "./event-C2eLiPpV.mjs";
4
- import { r as finalizeEvent, t as Keys } from "./key-DY_HEIpY.mjs";
5
- import { n as verifyEvent } from "./verifier-uC_jd-Nw.mjs";
6
- import { t as EventBuilder } from "./builder-CVRmUeNm.mjs";
7
- import { a as matchFilter } from "./filter-TrF2LEoy.mjs";
3
+ import { h as validateSignedEvent, t as compareEventsDesc, x as isTag } from "./event-B3ua2_oL.mjs";
4
+ import { r as finalizeEvent, t as Keys } from "./key-k8LDEPZh.mjs";
5
+ import { n as verifyEvent } from "./verifier-CjH1vnP2.mjs";
6
+ import { t as EventBuilder } from "./builder-BQbFDO6S.mjs";
7
+ import { a as matchFilter } from "./filter-B16P33dz.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-DENGQ_f6.mjs";
11
+ import { t as MemoryEventStore } from "./memory-VyPnOBU3.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) {
@@ -747,6 +747,17 @@ const eventStoreConformanceCases = [
747
747
  eq(ids(await store.query([{ ids: [target.id, tagged.id] }])).sort(), [tagged.id, target.id].sort(), "ids filter");
748
748
  }
749
749
  },
750
+ {
751
+ name: "multi-letter # keys are ignored (NIP-01 single-letter tag conditions only)",
752
+ async run(store) {
753
+ const tagged = note(alice(), "tagged", 1, [["client", "test-app"]]);
754
+ const plain = note(bob(), "plain", 2);
755
+ await store.putMany([tagged, plain]);
756
+ eq(await store.count([{ "#client": ["test-app"] }]), 2, "ignored on count");
757
+ eq(await store.count([{ "#client": ["other"] }]), 2, "ignored regardless of value");
758
+ eq(ids(await store.query([{ "#client": ["test-app"] }])).sort(), [plain.id, tagged.id].sort(), "ignored on query");
759
+ }
760
+ },
750
761
  {
751
762
  name: "count matches query cardinality across overlapping filters",
752
763
  async run(store) {
@@ -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: \"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};\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,5 +1,5 @@
1
1
  import { t as Event } from "./event-j3BwI5dF.mjs";
2
- import { t as Filter } from "./filter-CfEgQBXt.mjs";
2
+ import { t as Filter } from "./filter-PG7r79rt.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-Ccox7-5v.d.mts.map
47
+ //# sourceMappingURL=types-CqhnT-Mx.d.mts.map
@@ -95,6 +95,18 @@ function isRecord(value) {
95
95
  return typeof value === "object" && value !== null && !Array.isArray(value);
96
96
  }
97
97
  /**
98
+ * True when `value` contains a UTF-16 surrogate code unit without its other half (NIP-01 events
99
+ * reject these; `JSON.parse` accepts `\ud800`-style escapes, so validate after parsing).
100
+ * Char-by-char because Hermes may lack `String.prototype.isWellFormed`.
101
+ */
102
+ function hasLoneSurrogate(value) {
103
+ for (const ch of value) {
104
+ const code = ch.codePointAt(0);
105
+ if (code !== void 0 && code >= 55296 && code <= 57343) return true;
106
+ }
107
+ return false;
108
+ }
109
+ /**
98
110
  * Normalize a relay URL to a stable form: `http:`/`https:` are rewritten to `ws:`/`wss:` (a bare
99
111
  * host gets `wss://`), any other scheme throws UrlError. The result has a lowercased host, the
100
112
  * default port removed, duplicate path slashes collapsed, a sorted query, and no fragment. `URL`
@@ -134,6 +146,6 @@ function normalizeRelayUrls(urls) {
134
146
  return [...seen];
135
147
  }
136
148
  //#endregion
137
- export { EVENT_ID_BYTES as C, SUBSCRIPTION_ID_MAX_CHARS as D, SIGNATURE_BYTES as E, toError as S, SECRET_KEY_BYTES as T, MessageError as _, hexToBytes$1 as a, WasmPoisonedError as b, isRecord as c, nowSeconds as d, utf8Decoder as f, HexError as g, EventValidationError as h, bytesToHex$1 as i, normalizeRelayUrls as l, CryptoError as m, assertHex32 as n, isHex32 as o, utf8Encoder as p, assertSecretKeyBytes as r, isHex64 as s, assertByteLength as t, normalizeURL as u, NostrError as v, PUBLIC_KEY_BYTES as w, errorMessage as x, UrlError as y };
149
+ export { toError as C, SIGNATURE_BYTES as D, SECRET_KEY_BYTES as E, SUBSCRIPTION_ID_MAX_CHARS as O, errorMessage as S, PUBLIC_KEY_BYTES as T, HexError as _, hasLoneSurrogate as a, UrlError as b, isHex64 as c, normalizeURL as d, nowSeconds as f, EventValidationError as g, CryptoError as h, bytesToHex$1 as i, isRecord as l, utf8Encoder as m, assertHex32 as n, hexToBytes$1 as o, utf8Decoder as p, assertSecretKeyBytes as r, isHex32 as s, assertByteLength as t, normalizeRelayUrls as u, MessageError as v, EVENT_ID_BYTES as w, WasmPoisonedError as x, NostrError as y };
138
150
 
139
- //# sourceMappingURL=util-DMF9AIO0.mjs.map
151
+ //# sourceMappingURL=util-DDpPkiED.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util-DDpPkiED.mjs","names":["bytesToHex","nobleBytesToHex","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\n/** Wasm instance aborted; later calls on that instance fail. */\nexport class WasmPoisonedError extends NostrError {\n override name = \"WasmPoisonedError\";\n}\n","import {\n bytesToHex as nobleBytesToHex,\n hexToBytes as nobleHexToBytes,\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/** 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 // Iterating by code point skips proper pairs; only unpaired surrogates match.\n for (const ch of value) {\n const code = ch.codePointAt(0);\n if (code !== undefined && code >= 0xd800 && code <= 0xdfff) {\n return true;\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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"}
@@ -1,5 +1,5 @@
1
- import { a as hexToBytes, p as utf8Encoder } from "./util-DMF9AIO0.mjs";
2
- import { c as markVerified, h as validateSignedEvent, i as isMarkedVerified, r as isMarkedFailed, s as markUnverified, u as serializeValidatedEvent } from "./event-C2eLiPpV.mjs";
1
+ import { m as utf8Encoder, o as hexToBytes } from "./util-DDpPkiED.mjs";
2
+ import { c as markVerified, h as validateSignedEvent, i as isMarkedVerified, r as isMarkedFailed, s as markUnverified, u as serializeValidatedEvent } from "./event-B3ua2_oL.mjs";
3
3
  import { sha256 } from "@noble/hashes/sha2.js";
4
4
  import { schnorr } from "@noble/curves/secp256k1.js";
5
5
  //#region src/core/verifier.ts
@@ -35,4 +35,4 @@ const verifyEvent = createEventVerifier(nobleSerializedVerify);
35
35
  //#endregion
36
36
  export { verifyEvent as n, createEventVerifier as t };
37
37
 
38
- //# sourceMappingURL=verifier-uC_jd-Nw.mjs.map
38
+ //# sourceMappingURL=verifier-CjH1vnP2.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"verifier-uC_jd-Nw.mjs","names":[],"sources":["../src/core/verifier.ts"],"sourcesContent":["import { schnorr } from \"@noble/curves/secp256k1.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\n\nimport {\n isMarkedFailed,\n isMarkedVerified,\n markUnverified,\n markVerified,\n serializeValidatedEvent,\n validateSignedEvent,\n} from \"./event.ts\";\nimport type { Event } from \"./event.ts\";\nimport { hexToBytes, utf8Encoder } from \"./util.ts\";\n\n/**\n * Verification backend: return true iff `sha256(serializedUtf8)` equals `id` and `sig` is a valid\n * BIP-340 signature of `id` by the x-only `pubkey`. Inputs are raw bytes (`id`/`pubkey` 32, `sig`\n * 64). Exceptions propagate through the built verifier unmarked — a throwing backend is broken and\n * must surface.\n */\nexport type SerializedEventVerifier = (\n serializedUtf8: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n) => boolean;\n\n/** Whole-event verifier: return true iff the event's id and BIP-340 signature verify. */\nexport type EventVerifier = (event: Event) => boolean;\n\n/**\n * Build an event verifier with exactly the semantics of {@link verifyEvent}, delegating the hash and\n * signature check to `backend`. Verified/failed results share `verifyEvent`'s WeakSet caches, so\n * verifiers built this way and `verifyEvent` agree on (and reuse) each other's results.\n */\nexport function createEventVerifier(backend: SerializedEventVerifier): EventVerifier {\n return (event) => {\n if (isMarkedVerified(event)) {\n return true;\n }\n if (isMarkedFailed(event)) {\n return false;\n }\n if (!validateSignedEvent(event)) {\n markUnverified(event);\n return false;\n }\n const ok = backend(\n utf8Encoder.encode(serializeValidatedEvent(event)),\n hexToBytes(event.id),\n hexToBytes(event.pubkey),\n hexToBytes(event.sig),\n );\n if (ok) {\n markVerified(event);\n } else {\n markUnverified(event);\n }\n return ok;\n };\n}\n\nconst nobleSerializedVerify: SerializedEventVerifier = (serializedUtf8, id, pubkey, sig) => {\n try {\n const digest = sha256(serializedUtf8);\n if (!digest.every((byte, i) => byte === id[i])) {\n return false;\n }\n return schnorr.verify(sig, id, pubkey);\n } catch {\n return false;\n }\n};\n\n/** Verify event id and BIP-340 signature. Uses WeakSet cache (does not mutate the event). */\nexport const verifyEvent: EventVerifier = createEventVerifier(nobleSerializedVerify);\n"],"mappings":";;;;;;;;;;AAmCA,SAAgB,oBAAoB,SAAiD;CACnF,QAAQ,UAAU;EAChB,IAAI,iBAAiB,KAAK,GACxB,OAAO;EAET,IAAI,eAAe,KAAK,GACtB,OAAO;EAET,IAAI,CAAC,oBAAoB,KAAK,GAAG;GAC/B,eAAe,KAAK;GACpB,OAAO;EACT;EACA,MAAM,KAAK,QACT,YAAY,OAAO,wBAAwB,KAAK,CAAC,GACjD,WAAW,MAAM,EAAE,GACnB,WAAW,MAAM,MAAM,GACvB,WAAW,MAAM,GAAG,CACtB;EACA,IAAI,IACF,aAAa,KAAK;OAElB,eAAe,KAAK;EAEtB,OAAO;CACT;AACF;AAEA,MAAM,yBAAkD,gBAAgB,IAAI,QAAQ,QAAQ;CAC1F,IAAI;EAEF,IAAI,CADW,OAAO,cACZ,CAAC,CAAC,OAAO,MAAM,MAAM,SAAS,GAAG,EAAE,GAC3C,OAAO;EAET,OAAO,QAAQ,OAAO,KAAK,IAAI,MAAM;CACvC,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,MAAa,cAA6B,oBAAoB,qBAAqB"}
1
+ {"version":3,"file":"verifier-CjH1vnP2.mjs","names":[],"sources":["../src/core/verifier.ts"],"sourcesContent":["import { schnorr } from \"@noble/curves/secp256k1.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\n\nimport {\n isMarkedFailed,\n isMarkedVerified,\n markUnverified,\n markVerified,\n serializeValidatedEvent,\n validateSignedEvent,\n} from \"./event.ts\";\nimport type { Event } from \"./event.ts\";\nimport { hexToBytes, utf8Encoder } from \"./util.ts\";\n\n/**\n * Verification backend: return true iff `sha256(serializedUtf8)` equals `id` and `sig` is a valid\n * BIP-340 signature of `id` by the x-only `pubkey`. Inputs are raw bytes (`id`/`pubkey` 32, `sig`\n * 64). Exceptions propagate through the built verifier unmarked — a throwing backend is broken and\n * must surface.\n */\nexport type SerializedEventVerifier = (\n serializedUtf8: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n) => boolean;\n\n/** Whole-event verifier: return true iff the event's id and BIP-340 signature verify. */\nexport type EventVerifier = (event: Event) => boolean;\n\n/**\n * Build an event verifier with exactly the semantics of {@link verifyEvent}, delegating the hash and\n * signature check to `backend`. Verified/failed results share `verifyEvent`'s WeakSet caches, so\n * verifiers built this way and `verifyEvent` agree on (and reuse) each other's results.\n */\nexport function createEventVerifier(backend: SerializedEventVerifier): EventVerifier {\n return (event) => {\n if (isMarkedVerified(event)) {\n return true;\n }\n if (isMarkedFailed(event)) {\n return false;\n }\n if (!validateSignedEvent(event)) {\n markUnverified(event);\n return false;\n }\n const ok = backend(\n utf8Encoder.encode(serializeValidatedEvent(event)),\n hexToBytes(event.id),\n hexToBytes(event.pubkey),\n hexToBytes(event.sig),\n );\n if (ok) {\n markVerified(event);\n } else {\n markUnverified(event);\n }\n return ok;\n };\n}\n\nconst nobleSerializedVerify: SerializedEventVerifier = (serializedUtf8, id, pubkey, sig) => {\n try {\n const digest = sha256(serializedUtf8);\n if (!digest.every((byte, i) => byte === id[i])) {\n return false;\n }\n return schnorr.verify(sig, id, pubkey);\n } catch {\n return false;\n }\n};\n\n/** Verify event id and BIP-340 signature. Uses WeakSet cache (does not mutate the event). */\nexport const verifyEvent: EventVerifier = createEventVerifier(nobleSerializedVerify);\n"],"mappings":";;;;;;;;;;AAmCA,SAAgB,oBAAoB,SAAiD;CACnF,QAAQ,UAAU;EAChB,IAAI,iBAAiB,KAAK,GACxB,OAAO;EAET,IAAI,eAAe,KAAK,GACtB,OAAO;EAET,IAAI,CAAC,oBAAoB,KAAK,GAAG;GAC/B,eAAe,KAAK;GACpB,OAAO;EACT;EACA,MAAM,KAAK,QACT,YAAY,OAAO,wBAAwB,KAAK,CAAC,GACjD,WAAW,MAAM,EAAE,GACnB,WAAW,MAAM,MAAM,GACvB,WAAW,MAAM,GAAG,CACtB;EACA,IAAI,IACF,aAAa,KAAK;OAElB,eAAe,KAAK;EAEtB,OAAO;CACT;AACF;AAEA,MAAM,yBAAkD,gBAAgB,IAAI,QAAQ,QAAQ;CAC1F,IAAI;EAEF,IAAI,CADW,OAAO,cACZ,CAAC,CAAC,OAAO,MAAM,MAAM,SAAS,GAAG,EAAE,GAC3C,OAAO;EAET,OAAO,QAAQ,OAAO,KAAK,IAAI,MAAM;CACvC,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,MAAa,cAA6B,oBAAoB,qBAAqB"}
@@ -1,9 +1,9 @@
1
1
  import { t as Event } from "./event-j3BwI5dF.mjs";
2
- import { t as Filter } from "./filter-CfEgQBXt.mjs";
2
+ import { t as Filter } from "./filter-PG7r79rt.mjs";
3
3
  //#region src/core/message.d.ts
4
4
  /** NIP-01 subscription id: 1..64 chars. */
5
5
  type SubscriptionId = string;
6
- /** Validate a subscription id; throws {@link MessageError} unless 1..64 chars. */
6
+ /** Validate a subscription id; throws {@link MessageError} unless 1..64 Unicode scalar values. */
7
7
  declare function assertSubscriptionId(id: string): SubscriptionId;
8
8
  /** Validate the given id, or mint a random 16-hex-char subscription id. */
9
9
  declare function createSubscriptionId(id?: string): SubscriptionId;
@@ -55,4 +55,4 @@ declare function createEventVerifier(backend: SerializedEventVerifier): EventVer
55
55
  declare const verifyEvent: EventVerifier;
56
56
  //#endregion
57
57
  export { ClientMessage as a, SubscriptionId as c, encodeClientMessage as d, encodeRelayMessage as f, parseRelayMessage as h, verifyEvent as i, assertSubscriptionId as l, parseClientMessage as m, SerializedEventVerifier as n, CountResult as o, mergeCountHll as p, createEventVerifier as r, RelayMessage as s, EventVerifier as t, createSubscriptionId as u };
58
- //# sourceMappingURL=verifier-9c-bINrv.d.mts.map
58
+ //# sourceMappingURL=verifier-Cl24c9ph.d.mts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qntx/nostr",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Nostr protocol implementation and SDK in TypeScript",
5
5
  "keywords": [
6
6
  "decentralized",
@@ -1 +0,0 @@
1
- {"version":3,"file":"builder-CVRmUeNm.mjs","names":[],"sources":["../src/core/builder.ts"],"sourcesContent":["import { EventValidationError } from \"./error.ts\";\nimport type { Event, EventTemplate, UnsignedEvent } from \"./event.ts\";\nimport { finalizeEvent } from \"./key.ts\";\nimport type { Keys, SecretKeyInput } from \"./key.ts\";\nimport { Kind, isAddressableKind, isReplaceableKind } from \"./kind.ts\";\nimport { Tag, formatEventAddress, getDTag, parseEventAddress } from \"./tag.ts\";\nimport { normalizeURL, nowSeconds } from \"./util.ts\";\n\n/** NIP-18 e third entry MUST be a relay URL; empty string is not one. */\nfunction requireRelayUrl(relayHint: string | undefined): string {\n if (relayHint === undefined || relayHint === \"\") {\n throw new EventValidationError(\"relayHint must be a relay URL\");\n }\n return normalizeURL(relayHint);\n}\n\n/** Profile metadata JSON (kind 0 content). NIP-05 is not verified here. */\nexport type ProfileMetadata = {\n readonly name?: string | undefined;\n readonly display_name?: string | undefined;\n readonly about?: string | undefined;\n readonly picture?: string | undefined;\n readonly banner?: string | undefined;\n readonly website?: string | undefined;\n readonly nip05?: string | undefined;\n readonly lud06?: string | undefined;\n readonly lud16?: string | undefined;\n};\n\n/**\n * Fluent builder for event templates. Decouples intent (kind/content/tags) from signing (Keys /\n * NostrSigner).\n */\nexport class EventBuilder {\n #kind: number;\n #content: string;\n readonly #tags: Tag[] = [];\n #createdAt: number | undefined;\n\n constructor(kind: number, content = \"\") {\n this.#kind = kind;\n this.#content = content;\n }\n\n static textNote(content: string): EventBuilder {\n return new EventBuilder(Kind.TextNote, content);\n }\n\n static metadata(meta: ProfileMetadata): EventBuilder {\n return new EventBuilder(Kind.Metadata, JSON.stringify(meta));\n }\n\n static contacts(pubkeys: ReadonlyArray<string>): EventBuilder {\n const b = new EventBuilder(Kind.Contacts, \"\");\n for (const pk of pubkeys) {\n b.#tags.push(Tag.p(pk));\n }\n return b;\n }\n\n /**\n * Kind-5 deletion. Each target is a bare event id string (or `{ id, kind }`), or `{ address }`\n * for a `kind:pubkey:d` coordinate. Known kinds are emitted as deduped `k` tags (NIP-09 SHOULD).\n */\n static deletion(\n targets: ReadonlyArray<string | { id: string; kind?: number } | { address: string }>,\n reason = \"\",\n ): EventBuilder {\n const b = new EventBuilder(Kind.EventDeletion, reason);\n const kinds = new Set<number>();\n for (const target of targets) {\n if (typeof target === \"string\") {\n b.#tags.push(Tag.e(target));\n continue;\n }\n if (\"address\" in target) {\n const parsed = parseEventAddress(target.address);\n if (parsed) {\n kinds.add(parsed.kind);\n }\n b.#tags.push(Tag.a(target.address));\n continue;\n }\n b.#tags.push(Tag.e(target.id));\n if (target.kind !== undefined) {\n kinds.add(target.kind);\n }\n }\n for (const kind of kinds) {\n b.#tags.push(Tag.k(kind));\n }\n return b;\n }\n\n static reaction(target: Event, content = \"+\", opts?: { relayHint?: string }): EventBuilder {\n const hint = opts?.relayHint === undefined ? undefined : requireRelayUrl(opts.relayHint);\n let coord: string | undefined;\n if (isAddressableKind(target.kind)) {\n const d = getDTag(target.tags);\n if (d === undefined) {\n throw new EventValidationError(\"addressable event is missing d tag\");\n }\n coord = formatEventAddress(target.kind, target.pubkey, d);\n }\n\n const b = new EventBuilder(Kind.Reaction, content);\n // NIP-25 e is [e, id, relay, pubkey]; Tag.e third arg is a NIP-10 marker.\n b.#tags.push([\"e\", target.id.toLowerCase(), hint ?? \"\", target.pubkey.toLowerCase()]);\n b.#tags.push(Tag.p(target.pubkey, hint));\n b.#tags.push([\"k\", String(target.kind)]);\n if (coord !== undefined) {\n b.#tags.push(Tag.a(coord, hint));\n }\n return b;\n }\n\n static repost(target: Event, opts: { relayHint: string }): EventBuilder {\n if (target.kind !== Kind.TextNote) {\n throw new EventValidationError(\"non-kind-1 uses EventBuilder.genericRepost\");\n }\n const hint = requireRelayUrl(opts.relayHint);\n const content = target.tags.some((tag) => tag[0] === \"-\") ? \"\" : JSON.stringify(target);\n const b = new EventBuilder(Kind.Repost, content);\n b.#tags.push(Tag.e(target.id, hint));\n b.#tags.push(Tag.p(target.pubkey));\n return b;\n }\n\n static genericRepost(target: Event, opts: { relayHint: string; pPubkey?: string }): EventBuilder {\n if (target.kind === Kind.TextNote) {\n throw new EventValidationError(\"kind 1 uses EventBuilder.repost\");\n }\n const hint = requireRelayUrl(opts.relayHint);\n const replaceable = isReplaceableKind(target.kind);\n const addressable = isAddressableKind(target.kind);\n const d = getDTag(target.tags);\n if (addressable && d === undefined) {\n throw new EventValidationError(\"addressable event is missing d tag\");\n }\n\n const protectedTag = target.tags.some((tag) => tag[0] === \"-\");\n const content = protectedTag || replaceable || addressable ? \"\" : JSON.stringify(target);\n const b = new EventBuilder(Kind.GenericRepost, content);\n b.#tags.push(Tag.e(target.id, hint));\n b.#tags.push(Tag.p(opts.pPubkey ?? target.pubkey));\n b.#tags.push(Tag.k(target.kind));\n if (replaceable || addressable) {\n b.#tags.push(Tag.a(formatEventAddress(target.kind, target.pubkey, d ?? \"\")));\n }\n return b;\n }\n\n kind(kind: number): this {\n this.#kind = kind;\n return this;\n }\n\n content(content: string): this {\n this.#content = content;\n return this;\n }\n\n tag(tag: Tag): this {\n this.#tags.push(tag);\n return this;\n }\n\n tags(tags: Iterable<Tag>): this {\n for (const t of tags) {\n this.#tags.push(t);\n }\n return this;\n }\n\n createdAt(ts: number): this {\n this.#createdAt = ts;\n return this;\n }\n\n get currentKind(): number {\n return this.#kind;\n }\n\n get currentContent(): string {\n return this.#content;\n }\n\n get currentTags(): ReadonlyArray<Tag> {\n return this.#tags;\n }\n\n get currentCreatedAt(): number | undefined {\n return this.#createdAt;\n }\n\n /** Snapshot as EventTemplate (uses wall clock if created_at unset). */\n toTemplate(): EventTemplate {\n return {\n kind: this.#kind,\n content: this.#content,\n tags: [...this.#tags],\n created_at: this.#createdAt ?? nowSeconds(),\n };\n }\n\n /** Build unsigned event with the given pubkey. */\n buildUnsigned(pubkey: string): UnsignedEvent {\n const template = this.toTemplate();\n return {\n ...template,\n pubkey: pubkey.toLowerCase(),\n };\n }\n\n /** Sign with local Keys (synchronous). */\n signWithKeys(keys: Keys | SecretKeyInput): Event {\n return finalizeEvent(this.toTemplate(), keys);\n }\n\n /**\n * Sign via any NostrSigner-shaped object. Accepts a structural type so core does not depend on\n * the signer module.\n */\n async sign(signer: {\n getPublicKey: () => Promise<string>;\n signEvent: (unsigned: UnsignedEvent) => Promise<Event>;\n }): Promise<Event> {\n const pubkey = await signer.getPublicKey();\n return signer.signEvent(this.buildUnsigned(pubkey));\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"event-C2eLiPpV.mjs","names":[],"sources":["../src/core/tag.ts","../src/core/event.ts"],"sourcesContent":["import { isAddressableKind, isReplaceableKind } from \"./kind.ts\";\nimport { isHex32 } from \"./util.ts\";\n\n/** A NIP-01 tag: first element is the name, rest are values. */\nexport type Tag = ReadonlyArray<string>;\n\n/** Mutable tag builder input. */\nexport type TagInput = string[];\n\n/** Type guard: a non-empty array of strings. */\nexport function isTag(value: unknown): value is Tag {\n if (!Array.isArray(value) || value.length === 0) {\n return false;\n }\n for (const item of value) {\n if (typeof item !== \"string\") {\n return false;\n }\n }\n return true;\n}\n\n/** First element of a tag (its name). */\nexport function tagName(tag: Tag): string {\n return tag[0] ?? \"\";\n}\n\n/** Second element of a tag (its primary value), or undefined for a bare name tag. */\nexport function tagValue(tag: Tag): string | undefined {\n return tag[1];\n}\n\n/** Construct common tags. */\nexport const Tag = {\n e(id: string, relay?: string, marker?: string, pubkey?: string): Tag {\n const t: string[] = [\"e\", id.toLowerCase()];\n if (relay !== undefined) {\n t.push(relay);\n }\n if (marker !== undefined) {\n t.push(marker);\n }\n if (pubkey !== undefined) {\n t.push(pubkey.toLowerCase());\n }\n return t;\n },\n p(pubkey: string, relay?: string, petname?: string): Tag {\n const t: string[] = [\"p\", pubkey.toLowerCase()];\n if (relay !== undefined) {\n t.push(relay);\n }\n if (petname !== undefined) {\n t.push(petname);\n }\n return t;\n },\n a(coordinate: string, relay?: string): Tag {\n const t: string[] = [\"a\", coordinate];\n if (relay !== undefined) {\n t.push(relay);\n }\n return t;\n },\n d(identifier: string): Tag {\n return [\"d\", identifier];\n },\n t(hashtag: string): Tag {\n return [\"t\", hashtag];\n },\n r(url: string, marker?: string): Tag {\n const t: string[] = [\"r\", url];\n if (marker !== undefined) {\n t.push(marker);\n }\n return t;\n },\n k(kind: number | string): Tag {\n return [\"k\", String(kind)];\n },\n} as const;\n\n/** First tag value with the given name, or undefined when no such tag has a value. */\nexport function firstTagValue(tags: ReadonlyArray<Tag>, name: string): string | undefined {\n for (const tag of tags) {\n if (tag[0] === name && tag[1] !== undefined) {\n return tag[1];\n }\n }\n return undefined;\n}\n\n/** First `d` tag value on an event, if any. */\nexport function getDTag(tags: ReadonlyArray<Tag>): string | undefined {\n return firstTagValue(tags, \"d\");\n}\n\n/** NIP-01 `a` tag: `kind:pubkey:identifier` (identifier may be empty and may contain extra colons). */\nexport type EventAddress = {\n kind: number;\n pubkey: string;\n identifier: string;\n};\n\n/** Parse a NIP-01 addressable/replaceable coordinate. */\nexport function parseEventAddress(value: string): EventAddress | undefined {\n const first = value.indexOf(\":\");\n if (first <= 0) {\n return undefined;\n }\n const second = value.indexOf(\":\", first + 1);\n if (second === -1) {\n return undefined;\n }\n const kindText = value.slice(0, first);\n // Strict decimal: Number() also accepts \"1e4\", \"0x10\", \" 1\", \"+1\" — none are NIP-19 kinds.\n if (!/^\\d{1,5}$/.test(kindText)) {\n return undefined;\n }\n const kind = Number(kindText);\n if (kind > 65535) {\n return undefined;\n }\n const pubkey = value.slice(first + 1, second).toLowerCase();\n if (!isHex32(pubkey)) {\n return undefined;\n }\n return { kind, pubkey, identifier: value.slice(second + 1) };\n}\n\n/** Format an address as a `kind:pubkey:dTag` coordinate string. */\nexport function formatEventAddress(kind: number, pubkey: string, identifier = \"\"): string {\n return `${kind}:${pubkey.toLowerCase()}:${identifier}`;\n}\n\n/**\n * Coordinate for replaceable (`kind:pubkey:`) and addressable (`kind:pubkey:d`) events. Undefined\n * for regular/ephemeral kinds.\n */\nexport function eventAddress(event: {\n kind: number;\n pubkey: string;\n tags: ReadonlyArray<Tag>;\n}): string | undefined {\n if (isAddressableKind(event.kind)) {\n return formatEventAddress(event.kind, event.pubkey, getDTag(event.tags) ?? \"\");\n }\n if (isReplaceableKind(event.kind)) {\n return formatEventAddress(event.kind, event.pubkey, \"\");\n }\n return undefined;\n}\n","import { sha256 } from \"@noble/hashes/sha2.js\";\n\nimport { EventValidationError } from \"./error.ts\";\nimport type { Tag } from \"./tag.ts\";\nimport { isTag } from \"./tag.ts\";\nimport { isHex32, isHex64, isRecord, utf8Encoder, bytesToHex } from \"./util.ts\";\n\n/** Wire event template before pubkey/id/sig. */\nexport type EventTemplate = {\n readonly kind: number;\n readonly tags: ReadonlyArray<Tag>;\n readonly content: string;\n readonly created_at: number;\n};\n\n/** Event with pubkey, ready to hash and sign. */\nexport type UnsignedEvent = EventTemplate & {\n readonly pubkey: string;\n};\n\n/**\n * Fully formed NIP-01 event (JSON-friendly plain object). Fields are readonly at the type level;\n * wire interop remains plain JSON.\n */\nexport type Event = UnsignedEvent & {\n readonly id: string;\n readonly sig: string;\n};\n\n/** Cache of events whose signatures have been verified successfully. */\nconst verifiedEvents = new WeakSet<Event>();\n\n/** Cache of events known to fail verification. */\nconst failedEvents = new WeakSet<Event>();\n\nexport function markVerified(event: Event): void {\n verifiedEvents.add(event);\n failedEvents.delete(event);\n}\n\nexport function markUnverified(event: Event): void {\n failedEvents.add(event);\n verifiedEvents.delete(event);\n}\n\nexport function isMarkedVerified(event: Event): boolean {\n return verifiedEvents.has(event);\n}\n\nexport function isMarkedFailed(event: Event): boolean {\n return failedEvents.has(event);\n}\n\n/** Structural validation of an unsigned event (no crypto). */\nexport function validateEvent(event: unknown): event is UnsignedEvent {\n if (!isRecord(event)) {\n return false;\n }\n if (\n typeof event[\"kind\"] !== \"number\" ||\n !Number.isInteger(event[\"kind\"]) ||\n event[\"kind\"] < 0 ||\n event[\"kind\"] > 65535\n ) {\n return false;\n }\n if (typeof event[\"content\"] !== \"string\") {\n return false;\n }\n if (typeof event[\"created_at\"] !== \"number\" || !Number.isInteger(event[\"created_at\"])) {\n return false;\n }\n if (typeof event[\"pubkey\"] !== \"string\" || !isHex32(event[\"pubkey\"])) {\n return false;\n }\n if (!Array.isArray(event[\"tags\"])) {\n return false;\n }\n for (const tag of event[\"tags\"]) {\n if (!isTag(tag)) {\n return false;\n }\n }\n return true;\n}\n\n/** Structural validation of a full event including id and sig hex shape. */\nexport function validateSignedEvent(event: unknown): event is Event {\n if (!validateEvent(event)) {\n return false;\n }\n const e = event as Record<string, unknown>;\n if (typeof e[\"id\"] !== \"string\" || !isHex32(e[\"id\"])) {\n return false;\n }\n if (typeof e[\"sig\"] !== \"string\" || !isHex64(e[\"sig\"])) {\n return false;\n }\n return true;\n}\n\n/**\n * Canonical NIP-01 serialization used for hashing: JSON array `[0, pubkey, created_at, kind, tags,\n * content]` with no extra whitespace.\n */\nexport function serializeEvent(event: UnsignedEvent): string {\n if (!validateEvent(event)) {\n throw new EventValidationError(\"cannot serialize event with invalid shape\");\n }\n return serializeValidatedEvent(event);\n}\n\n/**\n * Canonical serialization for an event already known valid (after {@link validateEvent} or\n * {@link validateSignedEvent}); identical output to {@link serializeEvent} without re-walking the\n * structural checks.\n */\nexport function serializeValidatedEvent(event: UnsignedEvent): string {\n return JSON.stringify([0, event.pubkey, event.created_at, event.kind, event.tags, event.content]);\n}\n\n/** SHA-256 of the canonical serialization, lowercase hex. */\nexport function getEventHash(event: UnsignedEvent): string {\n const serialized = serializeEvent(event);\n return bytesToHex(sha256(utf8Encoder.encode(serialized)));\n}\n\n/** Id tie-break: plain code-unit order (ids are canonical lowercase hex, so `<` is lexical). */\nfunction compareIds(a: { id: string }, b: { id: string }): number {\n if (a.id < b.id) {\n return -1;\n }\n if (a.id > b.id) {\n return 1;\n }\n return 0;\n}\n\n/**\n * NIP-01 newest-first order: `created_at` descending, then `id` in plain code-unit order (ids are\n * canonical lowercase hex, so `<` is lexical order).\n */\nexport function compareEventsDesc(\n a: { id: string; created_at: number },\n b: { id: string; created_at: number },\n): number {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at;\n }\n return compareIds(a, b);\n}\n\n/**\n * Sort events reverse-chronologically by `created_at`, then by `id` lexicographically. Mutates and\n * returns the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort(compareEventsDesc);\n}\n\n/** Non-mutating sort; returns a new array. */\nexport function sortedEvents(events: ReadonlyArray<Event>): Event[] {\n return [...events].sort(compareEventsDesc);\n}\n\n/**\n * Negentropy item order: `created_at` ascending, then `id` lexicographic (`<`/`>`). Time axis is\n * opposite {@link sortEvents}; id tie-break is the same direction.\n */\nexport function itemCompare(\n a: { id: string; created_at: number },\n b: { id: string; created_at: number },\n): number {\n if (a.created_at !== b.created_at) {\n return a.created_at - b.created_at;\n }\n return compareIds(a, b);\n}\n\n/**\n * NIP-01 replaceable/addressable winner: higher `created_at`, or equal timestamp and\n * lexicographically lower `id`.\n */\nexport function isReplaceableWinner(\n candidate: { created_at: number; id: string },\n incumbent: { created_at: number; id: string },\n): boolean {\n if (candidate.created_at !== incumbent.created_at) {\n return candidate.created_at > incumbent.created_at;\n }\n return candidate.id < incumbent.id;\n}\n\n/** True when a signed event is the signed form of `unsigned` (kind/tags/content/time/pubkey). */\nexport function signedMatchesUnsigned(signed: Event, unsigned: UnsignedEvent): boolean {\n if (signed.kind !== unsigned.kind) {\n return false;\n }\n if (signed.content !== unsigned.content) {\n return false;\n }\n if (signed.created_at !== unsigned.created_at) {\n return false;\n }\n if (JSON.stringify(signed.tags) !== JSON.stringify(unsigned.tags)) {\n return false;\n }\n if (unsigned.pubkey && signed.pubkey.toLowerCase() !== unsigned.pubkey.toLowerCase()) {\n return false;\n }\n return true;\n}\n"],"mappings":"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"}