@qntx/nostr 0.8.0 → 0.10.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 (132) hide show
  1. package/LICENSE-APACHE +201 -0
  2. package/README.md +9 -19
  3. package/dist/{builder-aNU2gGFP.mjs → builder-CVRmUeNm.mjs} +4 -4
  4. package/dist/{builder-aNU2gGFP.mjs.map → builder-CVRmUeNm.mjs.map} +1 -1
  5. package/dist/{builder-CTwkB2zM.d.mts → builder-GBddxj8i.d.mts} +3 -3
  6. package/dist/{client-Cj2lUaVh.mjs → client-CeyDLoyO.mjs} +50 -28
  7. package/dist/client-CeyDLoyO.mjs.map +1 -0
  8. package/dist/client.d.mts +1 -1
  9. package/dist/client.mjs +1 -1
  10. package/dist/core.d.mts +7 -8
  11. package/dist/core.mjs +8 -8
  12. package/dist/{event-CHf47dPM.mjs → event-C2eLiPpV.mjs} +15 -5
  13. package/dist/event-C2eLiPpV.mjs.map +1 -0
  14. package/dist/{event-DJeb_DIL.d.mts → event-j3BwI5dF.d.mts} +2 -4
  15. package/dist/{filter-jI_9htF7.d.mts → filter-CfEgQBXt.d.mts} +3 -5
  16. package/dist/{filter-BQl3GlHt.mjs → filter-TrF2LEoy.mjs} +3 -36
  17. package/dist/filter-TrF2LEoy.mjs.map +1 -0
  18. package/dist/{gossip-DFxIBlsr.mjs → gossip-5ezg0Quo.mjs} +3 -3
  19. package/dist/gossip-5ezg0Quo.mjs.map +1 -0
  20. package/dist/{gossip-OOeeFzG5.d.mts → gossip-BWb9HmfK.d.mts} +8 -5
  21. package/dist/gossip.d.mts +1 -1
  22. package/dist/gossip.mjs +1 -1
  23. package/dist/http-CuazNRIP.mjs.map +1 -1
  24. package/dist/{http-C4HwIu4B.d.mts → http-kwWG-V1X.d.mts} +9 -3
  25. package/dist/{index-CPVEyzNX.d.mts → index-B1fLejg4.d.mts} +9 -9
  26. package/dist/{index-C_w2WfVz.d.mts → index-C0mFpelb.d.mts} +6 -6
  27. package/dist/{index-DyKwQnbt.d.mts → index-CLJEKFGd.d.mts} +4 -4
  28. package/dist/{index-BxCBX-8R.d.mts → index-CoX7SnO7.d.mts} +7 -4
  29. package/dist/{index-DU9tRFTZ.d.mts → index-Hp26bgYG.d.mts} +11 -4
  30. package/dist/{index-Cl_1XdYP.d.mts → index-uCYwXaaP.d.mts} +22 -14
  31. package/dist/index.d.mts +18 -19
  32. package/dist/index.mjs +17 -17
  33. package/dist/{key-D_5OgGT7.d.mts → key-CyKN1RTi.d.mts} +2 -2
  34. package/dist/{key-DnVpchl6.mjs → key-DY_HEIpY.mjs} +5 -4
  35. package/dist/key-DY_HEIpY.mjs.map +1 -0
  36. package/dist/{kind-Cxwhhkx8.mjs → kind-D8fENiiL.mjs} +11 -5
  37. package/dist/kind-D8fENiiL.mjs.map +1 -0
  38. package/dist/{loaders-BmPkQfzI.mjs → loaders-DifimjJ7.mjs} +9 -8
  39. package/dist/{loaders-BmPkQfzI.mjs.map → loaders-DifimjJ7.mjs.map} +1 -1
  40. package/dist/loaders.d.mts +2 -2
  41. package/dist/loaders.mjs +1 -1
  42. package/dist/{memory-CdJ07kub.mjs → memory-DENGQ_f6.mjs} +2 -2
  43. package/dist/{memory-CdJ07kub.mjs.map → memory-DENGQ_f6.mjs.map} +1 -1
  44. package/dist/{memory-index-Cbc83vOZ.mjs → memory-index-D_lvlYAy.mjs} +4 -4
  45. package/dist/{memory-index-Cbc83vOZ.mjs.map → memory-index-D_lvlYAy.mjs.map} +1 -1
  46. package/dist/{message-oNr7t0SJ.mjs → message-Rd5Z_17h.mjs} +5 -4
  47. package/dist/{message-oNr7t0SJ.mjs.map → message-Rd5Z_17h.mjs.map} +1 -1
  48. package/dist/{nip17-B5WgW4C4.d.mts → nip17-B7MI82Iq.d.mts} +4 -4
  49. package/dist/{nip51-CR0Q8eym.d.mts → nip51-O_vhOiK2.d.mts} +3 -3
  50. package/dist/{nip59-C-RWFQIv.d.mts → nip59-CdDoJptA.d.mts} +2 -2
  51. package/dist/{nip65-D1fbmhOg.d.mts → nip65-BL79HI8N.d.mts} +3 -3
  52. package/dist/{nip77-DzUwUxHq.d.mts → nip77-wHA_nEV5.d.mts} +2 -2
  53. package/dist/nips/blossom.d.mts +3 -3
  54. package/dist/nips/blossom.mjs +2 -2
  55. package/dist/nips/nip05.d.mts +1 -1
  56. package/dist/nips/nip10.d.mts +2 -2
  57. package/dist/nips/nip10.mjs +3 -3
  58. package/dist/nips/nip11.d.mts +1 -1
  59. package/dist/nips/nip13.d.mts +1 -1
  60. package/dist/nips/nip17.d.mts +1 -1
  61. package/dist/nips/nip17.mjs +3 -3
  62. package/dist/nips/nip27.d.mts +1 -1
  63. package/dist/nips/nip42.d.mts +1 -1
  64. package/dist/nips/nip42.mjs +1 -1
  65. package/dist/nips/nip51.d.mts +1 -1
  66. package/dist/nips/nip51.mjs +3 -3
  67. package/dist/nips/nip57.d.mts +1 -1
  68. package/dist/nips/nip57.mjs +3 -3
  69. package/dist/nips/nip59.d.mts +1 -1
  70. package/dist/nips/nip59.mjs +4 -4
  71. package/dist/nips/nip65.d.mts +1 -1
  72. package/dist/nips/nip65.mjs +2 -2
  73. package/dist/nips/nip77.d.mts +1 -1
  74. package/dist/nips/nip77.mjs +8 -5
  75. package/dist/nips/nip77.mjs.map +1 -1
  76. package/dist/nips/nip98.d.mts +1 -1
  77. package/dist/nips/nip98.mjs +3 -3
  78. package/dist/{pool-CqQvUnsq.d.mts → pool-Bfe3Tu40.d.mts} +7 -7
  79. package/dist/{pool-vlw74C4t.mjs → pool-jok2-iBJ.mjs} +28 -21
  80. package/dist/pool-jok2-iBJ.mjs.map +1 -0
  81. package/dist/{put-w13ci3sC.mjs → put-JsiCKMyZ.mjs} +4 -4
  82. package/dist/put-JsiCKMyZ.mjs.map +1 -0
  83. package/dist/{reactive-Tz0otDIP.mjs → reactive-6Wn9qf_d.mjs} +5 -5
  84. package/dist/{reactive-Tz0otDIP.mjs.map → reactive-6Wn9qf_d.mjs.map} +1 -1
  85. package/dist/{reactive-gGqUrzW8.d.mts → reactive-X4p6nABy.d.mts} +4 -4
  86. package/dist/{relay-quCs7MHD.mjs → relay-__QtO33h.mjs} +18 -10
  87. package/dist/relay-__QtO33h.mjs.map +1 -0
  88. package/dist/relay.d.mts +3 -3
  89. package/dist/relay.mjs +2 -2
  90. package/dist/{signer-poTGe6Er.mjs → signer-D7PtVvQn.mjs} +14 -7
  91. package/dist/signer-D7PtVvQn.mjs.map +1 -0
  92. package/dist/signer.d.mts +2 -2
  93. package/dist/signer.mjs +1 -1
  94. package/dist/{storage-B5IXTVuS.mjs → storage-oihjE0_x.mjs} +10 -10
  95. package/dist/{storage-B5IXTVuS.mjs.map → storage-oihjE0_x.mjs.map} +1 -1
  96. package/dist/storage.d.mts +2 -2
  97. package/dist/storage.mjs +3 -3
  98. package/dist/store.d.mts +1 -1
  99. package/dist/store.mjs +1 -1
  100. package/dist/testing.d.mts +8 -2
  101. package/dist/testing.mjs +32 -10
  102. package/dist/testing.mjs.map +1 -1
  103. package/dist/{types-DNmiscfd.d.mts → types-BJ4wZ7JJ.d.mts} +2 -2
  104. package/dist/{types-BacwnDtU.d.mts → types-Ccox7-5v.d.mts} +3 -3
  105. package/dist/{message-tclYCVNc.d.mts → verifier-9c-bINrv.d.mts} +23 -4
  106. package/dist/{verifier-B51Wkt5H.mjs → verifier-uC_jd-Nw.mjs} +3 -3
  107. package/dist/verifier-uC_jd-Nw.mjs.map +1 -0
  108. package/package.json +25 -50
  109. package/dist/client-Cj2lUaVh.mjs.map +0 -1
  110. package/dist/event-CHf47dPM.mjs.map +0 -1
  111. package/dist/filter-BQl3GlHt.mjs.map +0 -1
  112. package/dist/gossip-DFxIBlsr.mjs.map +0 -1
  113. package/dist/instance-C62w3cEt.mjs +0 -220
  114. package/dist/instance-C62w3cEt.mjs.map +0 -1
  115. package/dist/instance-DjsL8h84.d.mts +0 -15
  116. package/dist/key-DnVpchl6.mjs.map +0 -1
  117. package/dist/kind-Cxwhhkx8.mjs.map +0 -1
  118. package/dist/nostr_crypto_wasm_bg-Czrdty7Q.mjs +0 -4
  119. package/dist/nostr_crypto_wasm_bg.wasm +0 -0
  120. package/dist/pool-vlw74C4t.mjs.map +0 -1
  121. package/dist/put-w13ci3sC.mjs.map +0 -1
  122. package/dist/relay-quCs7MHD.mjs.map +0 -1
  123. package/dist/signer-poTGe6Er.mjs.map +0 -1
  124. package/dist/verifier-B51Wkt5H.mjs.map +0 -1
  125. package/dist/verifier-CgTFpgsC.d.mts +0 -22
  126. package/dist/wasm.browser.d.mts +0 -8
  127. package/dist/wasm.browser.mjs +0 -19
  128. package/dist/wasm.browser.mjs.map +0 -1
  129. package/dist/wasm.d.mts +0 -8
  130. package/dist/wasm.mjs +0 -28
  131. package/dist/wasm.mjs.map +0 -1
  132. /package/{LICENSE → LICENSE-MIT} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"instance-C62w3cEt.mjs","names":[],"sources":["../src/wasm/abi.ts","../src/wasm/adapter.ts","../src/wasm/instance.ts"],"sourcesContent":["/** Wasm-bindgen 0.2.122 bundler-produced exports used by interned TS glue. */\nexport type CryptoWasmExports = {\n memory: WebAssembly.Memory;\n verify: (\n idPtr: number,\n idLen: number,\n pkPtr: number,\n pkLen: number,\n sigPtr: number,\n sigLen: number,\n ) => number;\n verify_serialized: (\n serPtr: number,\n serLen: number,\n idPtr: number,\n idLen: number,\n pkPtr: number,\n pkLen: number,\n sigPtr: number,\n sigLen: number,\n ) => number;\n sign: (\n retptr: number,\n idPtr: number,\n idLen: number,\n skPtr: number,\n skLen: number,\n auxPtr: number,\n auxLen: number,\n ) => void;\n public_key: (retptr: number, skPtr: number, skLen: number) => void;\n /** 0.2.122 name for `__wbindgen_malloc(size, align)`. */\n __wbindgen_export: (size: number, align: number) => number;\n /** 0.2.122 name for `__wbindgen_free(ptr, size, align)` after malloc claimed `__wbindgen_export`. */\n __wbindgen_export2: (ptr: number, size: number, align: number) => void;\n __wbindgen_add_to_stack_pointer: (delta: number) => number;\n __wbindgen_start?: () => void;\n};\n\n/** Imports 0.2.122 may request. Instantiation fails if anything else appears. */\nexport const ALLOWED_WASM_IMPORTS: ReadonlyArray<{ module: string; name: string }> = [\n { module: \"wbg\", name: \"__wbindgen_throw\" },\n { module: \"./nostr_crypto_wasm_bg.js\", name: \"__wbindgen_throw\" },\n];\n\nconst utf8 = new TextDecoder();\n\nfunction decodeUtf8(memory: WebAssembly.Memory, ptr: number, len: number): string {\n return utf8.decode(new Uint8Array(memory.buffer, ptr, len));\n}\n\nfunction makeWbgImports(holder: { exports?: CryptoWasmExports }): WebAssembly.Imports {\n const throwRuntime = (ptr: number, len: number): never => {\n const mem = holder.exports?.memory;\n const msg = mem ? decodeUtf8(mem, ptr, len) : \"wasm panic\";\n throw new WebAssembly.RuntimeError(msg);\n };\n const wbg = {\n __wbindgen_throw: throwRuntime,\n };\n return {\n wbg,\n \"./nostr_crypto_wasm_bg.js\": wbg,\n };\n}\n\n/** Fail if the module imports anything outside {@link ALLOWED_WASM_IMPORTS}. */\nexport function assertAllowedWasmImports(mod: WebAssembly.Module): void {\n const allowed = new Set(ALLOWED_WASM_IMPORTS.map((item) => `${item.module}\\0${item.name}`));\n for (const imp of WebAssembly.Module.imports(mod)) {\n if (imp.kind !== \"function\") {\n throw new TypeError(`unexpected wasm import kind ${imp.kind}: ${imp.module}.${imp.name}`);\n }\n if (!allowed.has(`${imp.module}\\0${imp.name}`)) {\n throw new TypeError(`unexpected wasm import ${imp.module}.${imp.name}`);\n }\n }\n}\n\nfunction requireMemory(value: WebAssembly.ExportValue | undefined): WebAssembly.Memory {\n if (!(value instanceof WebAssembly.Memory)) {\n throw new TypeError(\"wasm missing memory export\");\n }\n return value;\n}\n\n// oxlint-disable-next-line no-unnecessary-type-parameters -- T binds the declared ABI signature at each call site\nfunction requireFn<T extends (...args: number[]) => unknown>(\n value: WebAssembly.ExportValue | undefined,\n name: string,\n): T {\n if (typeof value !== \"function\") {\n throw new TypeError(`wasm missing ${name} export`);\n }\n // oxlint-disable-next-line no-unsafe-type-assertion -- wasm exports are opaque; each name is bound to the signature the ABI contract requires\n return value as T;\n}\n\nfunction asExports(raw: WebAssembly.Exports): CryptoWasmExports {\n const exports: CryptoWasmExports = {\n memory: requireMemory(raw[\"memory\"]),\n verify: requireFn(raw[\"verify\"], \"verify\"),\n verify_serialized: requireFn(raw[\"verify_serialized\"], \"verify_serialized\"),\n sign: requireFn(raw[\"sign\"], \"sign\"),\n public_key: requireFn(raw[\"public_key\"], \"public_key\"),\n __wbindgen_export: requireFn(raw[\"__wbindgen_export\"], \"__wbindgen_export\"),\n __wbindgen_export2: requireFn(raw[\"__wbindgen_export2\"], \"__wbindgen_export2\"),\n __wbindgen_add_to_stack_pointer: requireFn(\n raw[\"__wbindgen_add_to_stack_pointer\"],\n \"__wbindgen_add_to_stack_pointer\",\n ),\n };\n const start = raw[\"__wbindgen_start\"];\n if (typeof start === \"function\") {\n exports.__wbindgen_start = requireFn<() => void>(start, \"__wbindgen_start\");\n }\n return exports;\n}\n\nfunction passBytes(exports: CryptoWasmExports, bytes: Uint8Array): { ptr: number; len: number } {\n const len = bytes.length;\n const ptr = exports.__wbindgen_export(len, 1) >>> 0;\n new Uint8Array(exports.memory.buffer, ptr, len).set(bytes);\n return { ptr, len };\n}\n\nexport function wasmVerify(\n exports: CryptoWasmExports,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n): boolean {\n const idP = passBytes(exports, id);\n const pkP = passBytes(exports, pubkey);\n const sigP = passBytes(exports, sig);\n return exports.verify(idP.ptr, idP.len, pkP.ptr, pkP.len, sigP.ptr, sigP.len) !== 0;\n}\n\nexport function wasmVerifySerialized(\n exports: CryptoWasmExports,\n serialized: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n): boolean {\n const serP = passBytes(exports, serialized);\n const idP = passBytes(exports, id);\n const pkP = passBytes(exports, pubkey);\n const sigP = passBytes(exports, sig);\n return (\n exports.verify_serialized(\n serP.ptr,\n serP.len,\n idP.ptr,\n idP.len,\n pkP.ptr,\n pkP.len,\n sigP.ptr,\n sigP.len,\n ) !== 0\n );\n}\n\nfunction takeBytes(exports: CryptoWasmExports, ptr: number, len: number): Uint8Array {\n const out = new Uint8Array(len);\n if (len > 0) {\n out.set(new Uint8Array(exports.memory.buffer, ptr, len));\n exports.__wbindgen_export2(ptr, len, 1);\n }\n return out;\n}\n\nfunction callReturningBytes(\n exports: CryptoWasmExports,\n invoke: (retptr: number) => void,\n): Uint8Array {\n const retptr = exports.__wbindgen_add_to_stack_pointer(-16);\n try {\n invoke(retptr);\n const view = new DataView(exports.memory.buffer);\n const ptr = view.getUint32(retptr, true);\n const len = view.getUint32(retptr + 4, true);\n return takeBytes(exports, ptr, len);\n } finally {\n exports.__wbindgen_add_to_stack_pointer(16);\n }\n}\n\nexport function wasmSign(\n exports: CryptoWasmExports,\n id: Uint8Array,\n seckey: Uint8Array,\n aux: Uint8Array,\n): Uint8Array {\n const idP = passBytes(exports, id);\n const skP = passBytes(exports, seckey);\n const auxP = passBytes(exports, aux);\n return callReturningBytes(exports, (retptr) => {\n exports.sign(retptr, idP.ptr, idP.len, skP.ptr, skP.len, auxP.ptr, auxP.len);\n });\n}\n\nexport function wasmPublicKey(exports: CryptoWasmExports, seckey: Uint8Array): Uint8Array {\n const skP = passBytes(exports, seckey);\n return callReturningBytes(exports, (retptr) => {\n exports.public_key(retptr, skP.ptr, skP.len);\n });\n}\n\nexport async function instantiateCryptoWasm(\n bytes: ArrayBuffer | ArrayBufferView,\n): Promise<CryptoWasmExports> {\n const holder: { exports?: CryptoWasmExports } = {};\n const { module, instance } = await WebAssembly.instantiate(bytes, makeWbgImports(holder));\n assertAllowedWasmImports(module);\n holder.exports = asExports(instance.exports);\n holder.exports.__wbindgen_start?.();\n return holder.exports;\n}\n","import { WasmPoisonedError } from \"../core/error.ts\";\nimport { createEventVerifier } from \"../core/verifier.ts\";\nimport type { EventVerifier, SerializedEventVerifier } from \"../core/verifier.ts\";\n\nexport { WasmPoisonedError } from \"../core/error.ts\";\n\n/**\n * Run `fn` unless the instance is already poisoned; a `WebAssembly.RuntimeError` aborts the\n * instance, so it is converted to {@link WasmPoisonedError} and rethrown on every later call.\n */\nexport function runPoisoned<T>(poison: { error?: Error }, fn: () => T): T {\n if (poison.error !== undefined) {\n throw poison.error;\n }\n try {\n return fn();\n } catch (error) {\n if (error instanceof WebAssembly.RuntimeError) {\n poison.error = new WasmPoisonedError(\"wasm instance aborted\", {\n cause: error,\n });\n throw poison.error;\n }\n throw error;\n }\n}\n\n/**\n * Build the WASM event verifier on {@link createEventVerifier}. A `WebAssembly.RuntimeError` from\n * the module means the instance aborted — it poisons every later call with the same\n * `WasmPoisonedError`; any other backend error verifies `false` (the event is marked failed).\n */\nexport function createWasmEventVerifier(\n verifySerialized: SerializedEventVerifier,\n poison: { error?: Error },\n): EventVerifier {\n const backend: SerializedEventVerifier = (serializedUtf8, id, pubkey, sig) => {\n try {\n return runPoisoned(poison, () => verifySerialized(serializedUtf8, id, pubkey, sig));\n } catch (error) {\n if (error instanceof WasmPoisonedError) {\n throw error;\n }\n return false;\n }\n };\n return createEventVerifier(backend);\n}\n","import { CryptoError } from \"../core/error.ts\";\nimport type { SigningBackend } from \"../core/key.ts\";\nimport type { EventVerifier } from \"../core/verifier.ts\";\nimport {\n instantiateCryptoWasm,\n wasmPublicKey,\n wasmSign,\n wasmVerify,\n wasmVerifySerialized,\n} from \"./abi.ts\";\nimport type { CryptoWasmExports } from \"./abi.ts\";\nimport { createWasmEventVerifier, runPoisoned } from \"./adapter.ts\";\n\nexport type LoadNostrWasmOptions = {\n /** Bytes, or a URL whose bytes will be read by the platform loader. */\n module?: ArrayBuffer | ArrayBufferView | URL | undefined;\n};\n\nexport type NostrWasm = SigningBackend & {\n verify: (id: Uint8Array, pubkey: Uint8Array, sig: Uint8Array) => boolean;\n verifySerialized: (\n serializedUtf8: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n ) => boolean;\n verifyEvent: EventVerifier;\n};\n\nexport function isWasmBytes(value: unknown): value is ArrayBuffer | ArrayBufferView {\n return value instanceof ArrayBuffer || ArrayBuffer.isView(value);\n}\n\nexport async function fetchWasmUrl(url: URL): Promise<Uint8Array> {\n const res = await fetch(url);\n if (!res.ok) {\n throw new CryptoError(`failed to fetch wasm: ${res.status} ${url.href}`);\n }\n return new Uint8Array(await res.arrayBuffer());\n}\n\nfunction requireByteLength(bytes: Uint8Array, expected: number, label: string): void {\n if (bytes.length !== expected) {\n throw new CryptoError(`invalid ${label} length: expected ${expected}, got ${bytes.length}`);\n }\n}\n\nfunction bindExports(exports: CryptoWasmExports): NostrWasm {\n const poison: { error?: Error } = {};\n const rawSerialized = (\n serializedUtf8: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n ): boolean => wasmVerifySerialized(exports, serializedUtf8, id, pubkey, sig);\n return {\n verify: (id, pubkey, sig) => runPoisoned(poison, () => wasmVerify(exports, id, pubkey, sig)),\n verifySerialized: (serializedUtf8, id, pubkey, sig) =>\n runPoisoned(poison, () => rawSerialized(serializedUtf8, id, pubkey, sig)),\n verifyEvent: createWasmEventVerifier(rawSerialized, poison),\n sign: (id, seckey, aux) =>\n runPoisoned(poison, () => {\n requireByteLength(id, 32, \"id\");\n requireByteLength(seckey, 32, \"secret key\");\n requireByteLength(aux, 32, \"aux\");\n const sig = wasmSign(exports, id, seckey, aux);\n if (sig.length !== 64) {\n throw new CryptoError(\"wasm sign failed\");\n }\n return sig;\n }),\n publicKey: (seckey) =>\n runPoisoned(poison, () => {\n requireByteLength(seckey, 32, \"secret key\");\n const pk = wasmPublicKey(exports, seckey);\n if (pk.length !== 32) {\n throw new CryptoError(\"wasm publicKey failed\");\n }\n return pk;\n }),\n };\n}\n\n/** Builds a loader that interns a single instance per module slot. */\nexport function createNostrWasmLoader(\n wasmBytes: (opts?: LoadNostrWasmOptions) => Promise<ArrayBuffer | ArrayBufferView>,\n): {\n loadNostrWasm: (opts?: LoadNostrWasmOptions) => Promise<NostrWasm>;\n resetNostrWasm: () => void;\n} {\n let interned: Promise<NostrWasm> | undefined;\n\n async function instantiate(opts?: LoadNostrWasmOptions): Promise<NostrWasm> {\n const bytes = await wasmBytes(opts);\n const exports = await instantiateCryptoWasm(bytes);\n return bindExports(exports);\n }\n\n /** Instantiate once. Repeats reuse the same module. Failure throws; no noble fallback. */\n async function loadNostrWasm(opts?: LoadNostrWasmOptions): Promise<NostrWasm> {\n if (interned) {\n return interned;\n }\n const pending = instantiate(opts);\n interned = pending;\n try {\n return await pending;\n } catch (error) {\n if (interned === pending) {\n interned = undefined;\n }\n throw error;\n }\n }\n\n function resetNostrWasm(): void {\n interned = undefined;\n }\n\n return { loadNostrWasm, resetNostrWasm };\n}\n"],"mappings":"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"}
@@ -1,15 +0,0 @@
1
- import { a as SigningBackend } from "./key-D_5OgGT7.mjs";
2
- import { t as EventVerifier } from "./verifier-CgTFpgsC.mjs";
3
- //#region src/wasm/instance.d.ts
4
- type LoadNostrWasmOptions = {
5
- /** Bytes, or a URL whose bytes will be read by the platform loader. */
6
- module?: ArrayBuffer | ArrayBufferView | URL | undefined;
7
- };
8
- type NostrWasm = SigningBackend & {
9
- verify: (id: Uint8Array, pubkey: Uint8Array, sig: Uint8Array) => boolean;
10
- verifySerialized: (serializedUtf8: Uint8Array, id: Uint8Array, pubkey: Uint8Array, sig: Uint8Array) => boolean;
11
- verifyEvent: EventVerifier;
12
- };
13
- //#endregion
14
- export { NostrWasm as n, LoadNostrWasmOptions as t };
15
- //# sourceMappingURL=instance-DjsL8h84.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"key-DnVpchl6.mjs","names":["hexToBytes","bytesToHex"],"sources":["../src/core/key.ts"],"sourcesContent":["import { schnorr } from \"@noble/curves/secp256k1.js\";\nimport { randomBytes } from \"@noble/hashes/utils.js\";\n\nimport { CryptoError, EventValidationError } from \"./error.ts\";\nimport { getEventHash, markVerified, validateEvent } from \"./event.ts\";\nimport type { Event, EventTemplate, UnsignedEvent } from \"./event.ts\";\nimport { assertHex32, assertSecretKeyBytes, bytesToHex, hexToBytes } from \"./util.ts\";\n\n/** 32-byte secret key held as bytes; prefer zeroize when done. */\nexport class SecretKey {\n #bytes: Uint8Array | undefined;\n\n private constructor(bytes: Uint8Array) {\n assertSecretKeyBytes(bytes);\n this.#bytes = new Uint8Array(bytes);\n }\n\n static generate(): SecretKey {\n return new SecretKey(schnorr.utils.randomSecretKey());\n }\n\n static fromBytes(bytes: Uint8Array): SecretKey {\n return new SecretKey(bytes);\n }\n\n static fromHex(hex: string): SecretKey {\n return new SecretKey(hexToBytes(assertHex32(hex, \"secret key\")));\n }\n\n get bytes(): Uint8Array {\n if (!this.#bytes) {\n throw new CryptoError(\"secret key has been zeroized\");\n }\n return new Uint8Array(this.#bytes);\n }\n\n toHex(): string {\n return bytesToHex(this.bytes);\n }\n\n /** Best-effort wipe of the internal buffer. */\n zeroize(): void {\n if (this.#bytes) {\n this.#bytes.fill(0);\n this.#bytes = undefined;\n }\n }\n}\n\n/** Anything a secret key can be given as: instance, raw bytes, or hex. */\nexport type SecretKeyInput = SecretKey | Uint8Array | string;\n\n/** Coerce a {@link SecretKeyInput} to {@link SecretKey}. */\nexport function toSecretKey(input: SecretKeyInput): SecretKey {\n if (input instanceof SecretKey) {\n return input;\n }\n if (typeof input === \"string\") {\n return SecretKey.fromHex(input);\n }\n return SecretKey.fromBytes(input);\n}\n\n/** Lowercase hex-encoded secp256k1 public key (x-only, 64 chars). */\nexport type PublicKey = string;\n\n/** Validate and normalize a hex public key; throws {@link HexError} on bad input. */\nexport function publicKeyFromHex(hex: string): PublicKey {\n return assertHex32(hex, \"public key\");\n}\n\n/**\n * BIP-340 signing backend. `publicKey` returns the 32-byte x-only public key of a 32-byte secret\n * key; `sign` returns the 64-byte BIP-340 signature of a 32-byte `id` using 32 bytes of auxiliary\n * randomness. Both throw on invalid input (for example an out-of-range secret key).\n */\nexport type SigningBackend = {\n publicKey: (secretKey: Uint8Array) => Uint8Array;\n sign: (id: Uint8Array, secretKey: Uint8Array, auxRand: Uint8Array) => Uint8Array;\n};\n\nconst nobleSigning: SigningBackend = {\n publicKey: (secretKey) => schnorr.getPublicKey(secretKey),\n sign: (id, secretKey, auxRand) => schnorr.sign(id, secretKey, auxRand),\n};\n\n/** Derive the public key for a secret key given as SecretKey, bytes, or hex. */\nexport function getPublicKey(secretKey: SecretKeyInput): PublicKey {\n return bytesToHex(schnorr.getPublicKey(toSecretKey(secretKey).bytes));\n}\n\n/** Keypair convenience wrapper. */\nexport class Keys {\n readonly secretKey: SecretKey;\n readonly publicKey: PublicKey;\n /**\n * BIP-340 backend that derived {@link publicKey} and signs in {@link finalizeEvent}/\n * {@link signEvent}. Defaults to noble; pass a loaded `NostrWasm` module or a native backend to\n * delegate signing.\n */\n readonly backend: SigningBackend;\n\n private constructor(secretKey: SecretKey, backend: SigningBackend) {\n this.secretKey = secretKey;\n this.backend = backend;\n const pubkey = backend.publicKey(secretKey.bytes);\n if (pubkey.length !== 32) {\n throw new CryptoError(\"signing backend returned an invalid public key\");\n }\n this.publicKey = bytesToHex(pubkey);\n }\n\n static generate(backend: SigningBackend = nobleSigning): Keys {\n return new Keys(SecretKey.generate(), backend);\n }\n\n static fromSecretKey(secretKey: SecretKeyInput, backend: SigningBackend = nobleSigning): Keys {\n return new Keys(toSecretKey(secretKey), backend);\n }\n}\n\nfunction resolveKeys(secretKey: SecretKeyInput | Keys): Keys {\n return secretKey instanceof Keys ? secretKey : Keys.fromSecretKey(secretKey);\n}\n\n/** Fill pubkey/id/sig on a template and return a signed event. */\nexport function finalizeEvent(template: EventTemplate, secretKey: SecretKeyInput | Keys): Event {\n const keys = resolveKeys(secretKey);\n const unsigned: UnsignedEvent = {\n kind: template.kind,\n tags: template.tags,\n content: template.content,\n created_at: template.created_at,\n pubkey: keys.publicKey,\n };\n return signEvent(unsigned, keys);\n}\n\n/**\n * Sign an already-assembled unsigned event. Rejects when `unsigned.pubkey` does not match the\n * secret key.\n */\nexport function signEvent(unsigned: UnsignedEvent, secretKey: SecretKeyInput | Keys): Event {\n const keys = resolveKeys(secretKey);\n if (!validateEvent(unsigned)) {\n throw new EventValidationError(\"cannot sign invalid unsigned event\");\n }\n\n if (unsigned.pubkey !== keys.publicKey) {\n throw new CryptoError(\"unsigned event pubkey does not match secret key\");\n }\n\n const normalized: UnsignedEvent = {\n kind: unsigned.kind,\n tags: unsigned.tags,\n content: unsigned.content,\n created_at: unsigned.created_at,\n pubkey: keys.publicKey,\n };\n\n const id = getEventHash(normalized);\n const sig = keys.backend.sign(hexToBytes(id), keys.secretKey.bytes, randomBytes(32));\n if (sig.length !== 64) {\n throw new CryptoError(\"signing backend returned an invalid signature\");\n }\n const event: Event = { ...normalized, id, sig: bytesToHex(sig) };\n markVerified(event);\n return event;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"kind-Cxwhhkx8.mjs","names":[],"sources":["../src/core/kind.ts"],"sourcesContent":["/** NIP-01 regular: stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n return kind === 1 || kind === 2 || (kind >= 4 && kind < 45) || (kind >= 1000 && kind < 10000);\n}\n\n/** Replaceable: latest per (pubkey, kind) wins. */\nexport function isReplaceableKind(kind: number): boolean {\n return kind === 0 || kind === 3 || (kind >= 10000 && kind < 20000);\n}\n\n/** Ephemeral: not expected to be stored. */\nexport function isEphemeralKind(kind: number): boolean {\n return kind >= 20000 && kind < 30000;\n}\n\n/** Addressable (parameterized replaceable): latest per (pubkey, kind, d-tag) wins. */\nexport function isAddressableKind(kind: number): boolean {\n return kind >= 30000 && kind < 40000;\n}\n\n/** NIP-01 kind classes: regular / replaceable / ephemeral / addressable. */\nexport type KindClassification =\n | \"regular\"\n | \"replaceable\"\n | \"ephemeral\"\n | \"addressable\"\n | \"unknown\";\n\n/** Classify a kind number into its NIP-01 storage class (`\"unknown\"` outside all ranges). */\nexport function classifyKind(kind: number): KindClassification {\n if (isRegularKind(kind)) {\n return \"regular\";\n }\n if (isReplaceableKind(kind)) {\n return \"replaceable\";\n }\n if (isEphemeralKind(kind)) {\n return \"ephemeral\";\n }\n if (isAddressableKind(kind)) {\n return \"addressable\";\n }\n return \"unknown\";\n}\n\n/** Kind numbers used by this package. */\nexport const Kind = {\n Metadata: 0,\n TextNote: 1,\n Contacts: 3,\n EventDeletion: 5,\n Repost: 6,\n Reaction: 7,\n Seal: 13,\n PrivateDirectMessage: 14,\n GenericRepost: 16,\n GiftWrap: 1059,\n ZapRequest: 9734,\n Zap: 9735,\n MuteList: 10000,\n PinList: 10001,\n RelayList: 10002,\n BookmarkList: 10003,\n FavoriteRelays: 10012,\n UserEmojiList: 10030,\n DirectMessageRelaysList: 10050,\n BlossomServerList: 10063,\n GiftWrapEphemeral: 21059,\n ClientAuth: 22242,\n NostrConnect: 24133,\n BlobsAuth: 24242,\n HttpAuth: 27235,\n RelaySets: 30002,\n EmojiSet: 30030,\n StarterPack: 39089,\n} as const;\n\n/** Name of a kind constant on {@link Kind}. */\nexport type KindName = keyof typeof Kind;\n/** Union of the numeric kind values on {@link Kind}. */\nexport type KnownKind = (typeof Kind)[KindName];\n"],"mappings":";;AACA,SAAgB,cAAc,MAAuB;CACnD,OAAO,SAAS,KAAK,SAAS,KAAM,QAAQ,KAAK,OAAO,MAAQ,QAAQ,OAAQ,OAAO;AACzF;;AAGA,SAAgB,kBAAkB,MAAuB;CACvD,OAAO,SAAS,KAAK,SAAS,KAAM,QAAQ,OAAS,OAAO;AAC9D;;AAGA,SAAgB,gBAAgB,MAAuB;CACrD,OAAO,QAAQ,OAAS,OAAO;AACjC;;AAGA,SAAgB,kBAAkB,MAAuB;CACvD,OAAO,QAAQ,OAAS,OAAO;AACjC;;AAWA,SAAgB,aAAa,MAAkC;CAC7D,IAAI,cAAc,IAAI,GACpB,OAAO;CAET,IAAI,kBAAkB,IAAI,GACxB,OAAO;CAET,IAAI,gBAAgB,IAAI,GACtB,OAAO;CAET,IAAI,kBAAkB,IAAI,GACxB,OAAO;CAET,OAAO;AACT;;AAGA,MAAa,OAAO;CAClB,UAAU;CACV,UAAU;CACV,UAAU;CACV,eAAe;CACf,QAAQ;CACR,UAAU;CACV,MAAM;CACN,sBAAsB;CACtB,eAAe;CACf,UAAU;CACV,YAAY;CACZ,KAAK;CACL,UAAU;CACV,SAAS;CACT,WAAW;CACX,cAAc;CACd,gBAAgB;CAChB,eAAe;CACf,yBAAyB;CACzB,mBAAmB;CACnB,mBAAmB;CACnB,YAAY;CACZ,cAAc;CACd,WAAW;CACX,UAAU;CACV,WAAW;CACX,UAAU;CACV,aAAa;AACf"}
@@ -1,4 +0,0 @@
1
- //#region \0wasm-url:/home/runner/work/nostr.js/nostr.js/src/wasm/nostr_crypto_wasm_bg.wasm
2
- var nostr_crypto_wasm_bg_default = new URL("nostr_crypto_wasm_bg.wasm", import.meta.url).href;
3
- //#endregion
4
- export { nostr_crypto_wasm_bg_default as default };
Binary file
@@ -1 +0,0 @@
1
- {"version":3,"file":"pool-vlw74C4t.mjs","names":[],"sources":["../src/relay/url.ts","../src/relay/fan-in.ts","../src/relay/pool.ts"],"sourcesContent":["import { normalizeURL } from \"../core/util.ts\";\n\n/**\n * {@link normalizeURL}, or the raw string when it is not a valid relay URL (ensureRelay then fails\n * it like a dead relay).\n */\nexport function relayUrlKey(url: string): string {\n try {\n return normalizeURL(url);\n } catch {\n return url;\n }\n}\n\n/** Unique relay keys in first-seen order. */\nexport function uniqueRelayUrls(urls: Iterable<string>): string[] {\n const seen = new Set<string>();\n const unique: string[] = [];\n for (const url of urls) {\n const key = relayUrlKey(url);\n if (seen.has(key)) {\n continue;\n }\n seen.add(key);\n unique.push(key);\n }\n return unique;\n}\n\n/**\n * True for `ws:` / `http:` URLs that are not `.onion`. Local-network `ws://` is still insecure;\n * allow those via a trusted set.\n */\nexport function isInsecureRelayUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n if (parsed.hostname.endsWith(\".onion\")) {\n return false;\n }\n return parsed.protocol === \"ws:\" || parsed.protocol === \"http:\";\n } catch {\n return false;\n }\n}\n","import { abortReason, onAbort, throwIfAborted } from \"../core/abort.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { invokeSafely } from \"../core/report.ts\";\nimport { RelayClosedError } from \"./error.ts\";\nimport type { Pool } from \"./pool.ts\";\nimport type { Relay } from \"./relay.ts\";\nimport type { Closer } from \"./subscription.ts\";\nimport { uniqueRelayUrls } from \"./url.ts\";\n\nexport type RoutedJob = {\n urls: ReadonlyArray<string>;\n filters: ReadonlyArray<Filter>;\n id?: string | undefined;\n};\n\nexport type FanInOptions = {\n /** First receipt only (deduped across jobs). `relayUrl` is the normalized relay URL. */\n onevent?: ((event: Event, relayUrl: string) => void) | undefined;\n oneose?: (() => void) | undefined;\n onclose?: ((reason: string) => void) | undefined;\n signal?: AbortSignal | undefined;\n /** Armed at fan-in only. Never forwarded to relay.subscribe. */\n eoseTimeoutMs?: number | undefined;\n alreadyHaveEvent?: ((id: string) => boolean) | undefined;\n /** Every receipt from every relay, including skipped duplicates. */\n receivedEvent?: ((id: string, relayUrl: string) => void) | undefined;\n closeOnEose?: boolean | undefined;\n /** Optional. ensureRelay uses Pool.#opts.connectTimeoutMs when omitted. */\n connectTimeoutMs?: number | undefined;\n};\n\n/**\n * Aggregate EOSE/close across routed jobs. One `seen` set. `pending` counts URL list entries\n * (duplicates included). `pendingEose` counts unique `jobIndex:url` keys.\n */\nexport function fanIn(pool: Pool, jobs: ReadonlyArray<RoutedJob>, opts: FanInOptions = {}): Closer {\n const seen = new Set<string>();\n const closers: Closer[] = [];\n let closed = false;\n let eoseFired = false;\n let sawEose = false;\n let eoseTimer: ReturnType<typeof setTimeout> | undefined;\n const eoseDone = new Set<string>();\n const eoseAttempted = new Set<string>();\n let pendingEose = 0;\n let pending = 0;\n\n const fireEose = () => {\n if (closed || eoseFired) {\n return;\n }\n eoseFired = true;\n if (eoseTimer !== undefined) {\n clearTimeout(eoseTimer);\n eoseTimer = undefined;\n }\n invokeSafely(() => opts.oneose?.());\n };\n\n /**\n * Remove one relay from the pending-EOSE set. `eosed` credits a real EOSE; a CLOSED or a connect\n * failure only drops the wait — the aggregate `oneose` fires once every remaining relay ended AND\n * at least one actually EOSE'd.\n */\n const settleEose = (jobIndex: number, url: string, eosed: boolean) => {\n const key = `${jobIndex}:${url}`;\n if (eoseDone.has(key)) {\n return;\n }\n eoseDone.add(key);\n if (eosed) {\n sawEose = true;\n }\n pendingEose -= 1;\n if (pendingEose === 0 && sawEose) {\n fireEose();\n }\n };\n\n const settleClose = () => {\n closed = true;\n disposeAbort();\n if (eoseTimer !== undefined) {\n clearTimeout(eoseTimer);\n eoseTimer = undefined;\n }\n };\n\n const closeAll = (reason?: string) => {\n if (closed) {\n return;\n }\n settleClose();\n for (const c of closers) {\n c.close(reason);\n }\n invokeSafely(() => opts.onclose?.(reason ?? \"closed by client\"));\n };\n\n if (opts.signal?.aborted === true) {\n closed = true;\n invokeSafely(() => opts.onclose?.(\"aborted\"));\n return { close: closeAll };\n }\n\n const disposeAbort = onAbort(opts.signal, () => closeAll(\"aborted\"));\n\n const failUrl = (jobIndex: number, key: string): void => {\n settleEose(jobIndex, key, false);\n pending -= 1;\n if (pending <= 0 && closers.length === 0 && !closed) {\n settleClose();\n invokeSafely(() => opts.onclose?.(\"all relays failed\"));\n }\n };\n\n const attach = (relay: Relay, job: RoutedJob, jobIndex: number): void => {\n if (closed) {\n return;\n }\n const received = opts.receivedEvent;\n const sub = relay.subscribe([...job.filters], {\n id: jobs.length === 1 ? job.id : undefined,\n closeOnEose: opts.closeOnEose,\n alreadyHaveEvent: (id) => {\n let have = seen.has(id);\n invokeSafely(() => {\n have = have || Boolean(opts.alreadyHaveEvent?.(id));\n });\n return have;\n },\n receivedEvent:\n received === undefined ? undefined : (id) => invokeSafely(() => received(id, relay.url)),\n onevent: (event) => {\n seen.add(event.id);\n invokeSafely(() => opts.onevent?.(event, relay.url));\n },\n oneose: () => settleEose(jobIndex, relay.url, true),\n onclose: (reason) => {\n settleEose(jobIndex, relay.url, false);\n pending -= 1;\n if (pending <= 0 && !closed) {\n settleClose();\n invokeSafely(() => opts.onclose?.(reason));\n }\n },\n });\n closers.push(sub);\n };\n\n for (const [jobIndex, job] of jobs.entries()) {\n // Equivalent spellings of one relay URL attach exactly once per job.\n for (const key of uniqueRelayUrls(job.urls)) {\n pending += 1;\n const eoseKey = `${jobIndex}:${key}`;\n if (!eoseAttempted.has(eoseKey)) {\n eoseAttempted.add(eoseKey);\n pendingEose += 1;\n }\n\n const tryAttach = (relay: Relay): void => {\n try {\n attach(relay, job, jobIndex);\n } catch (error) {\n if (error instanceof RelayClosedError) {\n failUrl(jobIndex, key);\n return;\n }\n throw error;\n }\n };\n\n void (async (): Promise<void> => {\n let relay: Relay;\n try {\n relay = await pool.ensureRelay(key, {\n signal: opts.signal,\n timeoutMs: opts.connectTimeoutMs,\n });\n } catch {\n // ensureRelay rejected only — not tryAttach throws (those must not look like connect failure).\n const existing = pool.getRelay(key);\n if (existing !== undefined) {\n tryAttach(existing);\n return;\n }\n failUrl(jobIndex, key);\n return;\n }\n tryAttach(relay);\n })();\n }\n }\n\n if (opts.eoseTimeoutMs !== undefined && pending > 0) {\n eoseTimer = setTimeout(() => {\n eoseTimer = undefined;\n fireEose();\n }, opts.eoseTimeoutMs);\n }\n\n if (pending === 0) {\n queueMicrotask(() => {\n if (closed) {\n return;\n }\n settleClose();\n invokeSafely(() => opts.onclose?.(\"no relays\"));\n });\n }\n\n return { close: closeAll };\n}\n\nexport async function fetchRouted(\n pool: Pool,\n jobs: ReadonlyArray<RoutedJob>,\n opts: {\n timeoutMs?: number | undefined;\n signal?: AbortSignal | undefined;\n connectTimeoutMs?: number | undefined;\n /** Every event of every relay batch, including cross-relay duplicates. */\n onevent?: ((event: Event, relayUrl: string) => void) | undefined;\n } = {},\n): Promise<Event[]> {\n throwIfAborted(opts.signal);\n const byId = new Map<string, Event>();\n await Promise.all(\n jobs.flatMap((job) => {\n return uniqueRelayUrls(job.urls).map(async (url) => {\n let batch: ReadonlyArray<Event>;\n let relayUrl: string;\n try {\n const relay = await pool.ensureRelay(url, {\n signal: opts.signal,\n timeoutMs: opts.connectTimeoutMs,\n });\n relayUrl = relay.url;\n const result = await relay.fetch([...job.filters], {\n timeoutMs: opts.timeoutMs,\n signal: opts.signal,\n });\n batch = result.events;\n } catch {\n // An abort rejects the whole call; per-relay failures are skipped.\n if (opts.signal?.aborted === true) {\n throw abortReason(opts.signal);\n }\n return;\n }\n // The whole batch lands before callbacks so a throwing onevent cannot\n // drop events; listener errors are reported, never propagated.\n for (const event of batch) {\n if (!byId.has(event.id)) {\n byId.set(event.id, event);\n }\n }\n for (const event of batch) {\n invokeSafely(() => opts.onevent?.(event, relayUrl));\n }\n });\n }),\n );\n return [...byId.values()];\n}\n","import { abortReason, throwIfAborted } from \"../core/abort.ts\";\nimport { Emitter } from \"../core/emitter.ts\";\nimport { MessageError, toError } from \"../core/error.ts\";\nimport type { Event, EventTemplate } from \"../core/event.ts\";\nimport { canonicalizeFilters } from \"../core/filter.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { assertSubscriptionId } from \"../core/message.ts\";\nimport type { CountResult } from \"../core/message.ts\";\nimport { normalizeURL } from \"../core/util.ts\";\nimport { RelayConnectionError, RelayPublishError, RelaySuspendedError } from \"./error.ts\";\nimport { fanIn, fetchRouted } from \"./fan-in.ts\";\nimport { Relay, RelayStatus } from \"./relay.ts\";\nimport type { RelayFetchEnd, RelayOptions, SubscribeOptions } from \"./relay.ts\";\nimport type { Closer } from \"./subscription.ts\";\nimport { isInsecureRelayUrl, uniqueRelayUrls } from \"./url.ts\";\n\n/** Per-relay tolerance for events that fail id/signature verification. */\nexport type InvalidEventPolicy = {\n /** Invalid events tolerated within `windowMs`; exceeding it suspends the relay. */\n limit: number;\n windowMs: number;\n /** How long a suspended relay stays closed before it may reconnect. */\n cooldownMs: number;\n};\n\n/** Pool-wide options applied to every managed relay. */\nexport type PoolOptions = Omit<RelayOptions, \"authSigner\"> & {\n /**\n * When set, automatically answer NIP-42 AUTH challenges for relays that send them. Return\n * undefined to skip a given relay URL.\n */\n automaticallyAuth?:\n | ((relayURL: string) => ((event: EventTemplate) => Promise<Event>) | undefined)\n | undefined;\n /** When false (default), ensureRelay rejects isInsecureRelayUrl unless trusted. */\n allowInsecure?: boolean | undefined;\n trustedInsecureUrls?: ReadonlyArray<string> | undefined;\n /** Close unused relays. Unset/0 = disabled. */\n idleTimeoutMs?: number | undefined;\n idleCleanupIntervalMs?: number | undefined;\n /**\n * Soft cap on connected non-pinned relays. When `ensureRelay` would create a new non-pinned relay\n * at the cap, the least-recently-used idle one is closed first (idle = no subscriptions and no\n * in-flight requests). If none is idle the connect proceeds anyway — the cap never fails a\n * request.\n */\n maxRelays?: number | undefined;\n /** Normalized URLs never closed by idle cleanup or `maxRelays` eviction. */\n pinnedUrls?: ReadonlyArray<string> | undefined;\n /**\n * When set, a relay whose `limit`-th-plus-one EVENT fails id/signature verification inside a\n * `windowMs` sliding window is disconnected and suspended for `cooldownMs`. During suspension\n * `ensureRelay` rejects with {@link RelaySuspendedError}; once it lifts, live subscriptions resume\n * through the normal reconnect path. Unset = events are dropped without counting, as before.\n */\n invalidEventPolicy?: InvalidEventPolicy | undefined;\n};\n\n/** Typed event payloads for {@link Pool.on}. */\nexport type PoolEventMap = {\n /** Normalized URLs closed by an idle-cleanup pass. */\n idle: ReadonlyArray<string>;\n /**\n * A relay suspended for exceeding `invalidEventPolicy`: `url` is the normalized relay URL and\n * `until` the epoch-ms time the suspension lifts.\n */\n suspend: { readonly url: string; readonly until: number };\n};\n\n/** Per-relay publish outcome: the relay's OK verdict or a transport-level failure. */\nexport type PoolPublishResult =\n | { readonly url: string; readonly status: \"ok\"; readonly message: string }\n | { readonly url: string; readonly status: \"rejected\"; readonly message: string }\n | { readonly url: string; readonly status: \"failed\"; readonly error: Error };\n\n/** Multi-relay subscribe options: callbacks also receive the normalized relay URL. */\nexport type PoolSubscribeOptions = Omit<SubscribeOptions, \"onevent\" | \"receivedEvent\"> & {\n onevent?: ((event: Event, relayUrl: string) => void) | undefined;\n receivedEvent?: ((id: string, relayUrl: string) => void) | undefined;\n};\n\n/** Per-relay one-shot fetch outcome: collected events plus how its REQ ended. */\nexport type PoolFetchResult = {\n readonly url: string;\n readonly events: ReadonlyArray<Event>;\n readonly end: RelayFetchEnd | { readonly type: \"failed\"; readonly error: Error };\n};\n\n/** Per-relay NIP-45 COUNT result (or a transport-level failure). */\nexport type PoolCountResult =\n | ({ readonly url: string; readonly status: \"ok\" } & CountResult)\n | { readonly url: string; readonly status: \"failed\"; readonly error: Error };\n\nconst countOk = (url: string, payload: CountResult): PoolCountResult => ({\n url,\n status: \"ok\",\n ...payload,\n});\n\nfunction isIdle(relay: Relay): boolean {\n return (\n relay.status !== RelayStatus.Connecting &&\n relay.subscriptionCount === 0 &&\n relay.inFlightCount === 0\n );\n}\n\n/** Multi-relay coordinator: connection reuse, cross-relay event dedup, fan-out publish. */\nexport class Pool {\n readonly #relays = new Map<string, Relay>();\n readonly #relayOptions: Omit<RelayOptions, \"authSigner\">;\n readonly #automaticallyAuth: PoolOptions[\"automaticallyAuth\"];\n readonly #maxRelays: number | undefined;\n readonly #invalidEventPolicy: InvalidEventPolicy | undefined;\n readonly #events = new Emitter<PoolEventMap>();\n readonly #lastActivity = new Map<string, number>();\n readonly #idleTimeoutMs: number;\n #idleTimer: ReturnType<typeof setInterval> | undefined;\n #allowInsecure: boolean;\n #trustedInsecure: Set<string>;\n #pinned: Set<string>;\n /** Per-relay timestamps of verification failures inside the sliding window. */\n readonly #invalidEvents = new Map<string, number[]>();\n /** Normalized URL → epoch ms when its suspension lifts. */\n readonly #suspendedUntil = new Map<string, number>();\n readonly #resumeTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\n constructor(opts: PoolOptions = {}) {\n const {\n automaticallyAuth,\n allowInsecure,\n trustedInsecureUrls,\n idleTimeoutMs,\n idleCleanupIntervalMs,\n maxRelays,\n pinnedUrls,\n invalidEventPolicy,\n ...relayOptions\n } = opts;\n this.#relayOptions = relayOptions;\n this.#automaticallyAuth = automaticallyAuth;\n this.#maxRelays = maxRelays;\n this.#invalidEventPolicy = invalidEventPolicy;\n this.#allowInsecure = allowInsecure ?? false;\n this.#trustedInsecure = new Set((trustedInsecureUrls ?? []).map(normalizeURL));\n this.#pinned = new Set((pinnedUrls ?? []).map(normalizeURL));\n this.#idleTimeoutMs = idleTimeoutMs ?? 0;\n if (this.#idleTimeoutMs > 0) {\n this.#idleTimer = setInterval(() => this.cleanIdleRelays(), idleCleanupIntervalMs ?? 30_000);\n }\n }\n\n /**\n * Listen for a pool event. Multiple listeners per type are allowed and fire in registration\n * order; a throwing listener is reported and does not break the dispatch. Returns an unsubscribe\n * function.\n */\n on<K extends keyof PoolEventMap>(\n type: K,\n listener: (payload: PoolEventMap[K]) => void,\n ): () => void {\n return this.#events.on(type, listener);\n }\n\n setAllowInsecure(allow: boolean): void {\n this.#allowInsecure = allow;\n }\n\n setTrustedInsecureUrls(urls: ReadonlyArray<string>): void {\n this.#trustedInsecure = new Set(urls.map(normalizeURL));\n }\n\n setPinnedUrls(urls: ReadonlyArray<string>): void {\n this.#pinned = new Set(urls.map(normalizeURL));\n }\n\n /**\n * Drop cached AUTH rejections and re-fire pending challenges on every pooled relay, so a later\n * `setSigner` takes effect without reconnect.\n */\n resetAuth(): void {\n for (const relay of this.#relays.values()) {\n relay.resetAuth();\n }\n }\n\n cleanIdleRelays(): void {\n if (this.#idleTimeoutMs <= 0) {\n return;\n }\n const now = Date.now();\n const idle: string[] = [];\n for (const [url, relay] of this.#relays) {\n if (this.#pinned.has(url) || !isIdle(relay)) {\n continue;\n }\n const last = this.#lastActivity.get(url) ?? 0;\n if (!relay.connected || now - last >= this.#idleTimeoutMs) {\n idle.push(url);\n }\n }\n for (const url of this.#lastActivity.keys()) {\n if (!this.#relays.has(url)) {\n this.#lastActivity.delete(url);\n }\n }\n if (idle.length === 0) {\n return;\n }\n this.closeRelays(idle);\n this.#events.emit(\"idle\", idle);\n }\n\n #touch(url: string): void {\n this.#lastActivity.set(url, Date.now());\n }\n\n /**\n * Count one verification failure for `norm`; on the `limit + 1`-th inside `windowMs` suspend the\n * relay: drop its connection (subscriptions kept), record `until`, and emit `suspend`.\n */\n #noteInvalidEvent(norm: string): void {\n const policy = this.#invalidEventPolicy;\n if (policy === undefined) {\n return;\n }\n const now = Date.now();\n const window = (this.#invalidEvents.get(norm) ?? []).filter((at) => now - at < policy.windowMs);\n window.push(now);\n if (window.length <= policy.limit) {\n this.#invalidEvents.set(norm, window);\n return;\n }\n this.#invalidEvents.delete(norm);\n if (this.#suspendedUntil.has(norm)) {\n return;\n }\n const until = now + policy.cooldownMs;\n this.#suspendedUntil.set(norm, until);\n this.#relays.get(norm)?.disconnect();\n const resume = setTimeout(() => {\n this.#resumeTimers.delete(norm);\n this.#suspendedUntil.delete(norm);\n const relay = this.#relays.get(norm);\n if (relay === undefined || relay.connected || relay.subscriptionCount === 0) {\n return;\n }\n void (async (): Promise<void> => {\n try {\n await relay.connect();\n } catch {\n // With enableReconnect the relay's own backoff retries; otherwise the\n // next ensureRelay attempt reconnects on demand.\n }\n })();\n }, policy.cooldownMs);\n this.#resumeTimers.set(norm, resume);\n this.#events.emit(\"suspend\", { url: norm, until });\n }\n\n #rejectInsecure(url: string, norm: string): void {\n if (this.#allowInsecure) {\n return;\n }\n if (!isInsecureRelayUrl(url)) {\n return;\n }\n if (this.#trustedInsecure.has(norm)) {\n return;\n }\n throw new RelayConnectionError(\"insecure relay connection blocked\", norm);\n }\n\n #stopIdleCleanup(): void {\n if (this.#idleTimer === undefined) {\n return;\n }\n clearInterval(this.#idleTimer);\n this.#idleTimer = undefined;\n }\n\n /**\n * Soft cap: closing the least-recently-used idle non-pinned relay when a new non-pinned relay\n * would push the count past `maxRelays`. Busy relays are never evicted; with none idle the cap is\n * exceeded rather than failing.\n */\n #enforceMaxRelays(incoming: string): void {\n const cap = this.#maxRelays;\n if (cap === undefined || this.#pinned.has(incoming)) {\n return;\n }\n let count = 0;\n let oldestUrl: string | undefined;\n let oldestAt = Number.POSITIVE_INFINITY;\n for (const [url, relay] of this.#relays) {\n if (this.#pinned.has(url)) {\n continue;\n }\n count += 1;\n if (!isIdle(relay)) {\n continue;\n }\n const at = this.#lastActivity.get(url) ?? 0;\n if (at < oldestAt) {\n oldestAt = at;\n oldestUrl = url;\n }\n }\n if (count >= cap && oldestUrl !== undefined) {\n this.closeRelays([oldestUrl]);\n }\n }\n\n async ensureRelay(\n url: string,\n opts?: { signal?: AbortSignal | undefined; timeoutMs?: number | undefined },\n ): Promise<Relay> {\n const norm = normalizeURL(url);\n this.#rejectInsecure(url, norm);\n const suspendedUntil = this.#suspendedUntil.get(norm);\n if (suspendedUntil !== undefined) {\n if (Date.now() < suspendedUntil) {\n throw new RelaySuspendedError(norm, suspendedUntil);\n }\n this.#suspendedUntil.delete(norm);\n }\n let relay = this.#relays.get(norm);\n if (!relay) {\n this.#enforceMaxRelays(norm);\n const signFn = this.#automaticallyAuth?.(norm) ?? undefined;\n const created = new Relay(norm, { ...this.#relayOptions, authSigner: signFn });\n // Only drop from the pool on terminal close (reconnect keeps the entry).\n created.on(\"close\", () => {\n this.#relays.delete(norm);\n this.#lastActivity.delete(norm);\n });\n created.on(\"invalidevent\", () => {\n this.#noteInvalidEvent(norm);\n });\n if (signFn) {\n created.on(\"auth\", () => {\n void (async (): Promise<void> => {\n try {\n await created.auth(signFn);\n } catch {\n // auth failure surfaces on subsequent CLOSED/OK; avoid unhandled rejection\n }\n })();\n });\n }\n this.#relays.set(norm, created);\n relay = created;\n }\n this.#touch(norm);\n if (!relay.connected) {\n try {\n await relay.connect({\n signal: opts?.signal,\n timeoutMs: opts?.timeoutMs ?? this.#relayOptions.connectTimeoutMs,\n });\n } catch (error) {\n // Keep the entry when reconnect is enabled so open subscriptions can recover.\n if (this.#relayOptions.enableReconnect !== true) {\n this.#relays.delete(norm);\n this.#lastActivity.delete(norm);\n }\n throw error;\n }\n }\n return relay;\n }\n\n /** Close every pooled relay and clear all pool state. */\n close(): void {\n this.#stopIdleCleanup();\n for (const timer of this.#resumeTimers.values()) {\n clearTimeout(timer);\n }\n this.#resumeTimers.clear();\n this.#suspendedUntil.clear();\n this.#invalidEvents.clear();\n for (const relay of this.#relays.values()) {\n relay.close();\n }\n this.#relays.clear();\n this.#lastActivity.clear();\n }\n\n /** Close and forget the listed relays; other pooled relays are untouched. */\n closeRelays(urls: ReadonlyArray<string>): void {\n for (const url of urls) {\n const norm = normalizeURL(url);\n const timer = this.#resumeTimers.get(norm);\n if (timer !== undefined) {\n clearTimeout(timer);\n this.#resumeTimers.delete(norm);\n }\n this.#relays.get(norm)?.close();\n this.#relays.delete(norm);\n this.#lastActivity.delete(norm);\n }\n }\n\n /**\n * Subscribe across relays. Deduplicates by event id. Returns a closer; callbacks receive every\n * new event once.\n */\n subscribe(\n relays: ReadonlyArray<string>,\n filters: ReadonlyArray<Filter>,\n opts: PoolSubscribeOptions = {},\n ): Closer {\n if (filters.length === 0) {\n throw new MessageError(\"REQ requires at least one filter\");\n }\n if (opts.id !== undefined) {\n assertSubscriptionId(opts.id);\n }\n const canonical = canonicalizeFilters(filters);\n return fanIn(this, [{ urls: relays, filters: canonical, id: opts.id }], {\n onevent: opts.onevent,\n oneose: opts.oneose,\n onclose: opts.onclose,\n signal: opts.signal,\n eoseTimeoutMs: opts.eoseTimeoutMs,\n alreadyHaveEvent: opts.alreadyHaveEvent,\n receivedEvent: opts.receivedEvent,\n closeOnEose: opts.closeOnEose,\n connectTimeoutMs: this.#relayOptions.connectTimeoutMs,\n });\n }\n\n /**\n * One-shot fetch per relay: each entry reports its own events and end reason (`eose`, `closed`,\n * `timeout`), or `failed` when the connect or REQ itself errored. An abort rejects the whole\n * call. Events are not deduped across relays — use {@link fetch} for that.\n */\n async fetchEach(\n relays: ReadonlyArray<string>,\n filters: ReadonlyArray<Filter>,\n opts?: { timeoutMs?: number | undefined; signal?: AbortSignal | undefined },\n ): Promise<PoolFetchResult[]> {\n if (filters.length === 0) {\n throw new MessageError(\"REQ requires at least one filter\");\n }\n throwIfAborted(opts?.signal);\n const canonical = canonicalizeFilters(filters);\n return Promise.all(\n uniqueRelayUrls(relays).map(async (url): Promise<PoolFetchResult> => {\n try {\n const relay = await this.ensureRelay(url, {\n signal: opts?.signal,\n timeoutMs: this.#relayOptions.connectTimeoutMs,\n });\n this.#touch(relay.url);\n const result = await relay.fetch([...canonical], {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n });\n return { url, events: result.events, end: result.end };\n } catch (error) {\n // An abort rejects the whole call; per-relay failures are reported.\n if (opts?.signal?.aborted === true) {\n throw abortReason(opts.signal);\n }\n return {\n url,\n events: [],\n end: { type: \"failed\", error: toError(error) },\n };\n }\n }),\n );\n }\n\n /** Fetch events until each connected relay EOSE or timeout; dedupe by id. */\n async fetch(\n relays: ReadonlyArray<string>,\n filters: ReadonlyArray<Filter>,\n opts?: {\n timeoutMs?: number | undefined;\n signal?: AbortSignal | undefined;\n /** Every event of every relay batch, including cross-relay duplicates. */\n onevent?: ((event: Event, relayUrl: string) => void) | undefined;\n },\n ): Promise<Event[]> {\n if (filters.length === 0) {\n throw new MessageError(\"REQ requires at least one filter\");\n }\n return fetchRouted(this, [{ urls: relays, filters: canonicalizeFilters(filters) }], {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n connectTimeoutMs: this.#relayOptions.connectTimeoutMs,\n onevent: opts?.onevent,\n });\n }\n\n /** Publish to all listed relays; returns per-relay outcomes. */\n async publish(\n relays: ReadonlyArray<string>,\n event: Event,\n opts?: { timeoutMs?: number | undefined },\n ): Promise<PoolPublishResult[]> {\n return Promise.all(\n uniqueRelayUrls(relays).map(async (url): Promise<PoolPublishResult> => {\n try {\n const relay = await this.ensureRelay(url, {\n timeoutMs: this.#relayOptions.connectTimeoutMs,\n });\n this.#touch(relay.url);\n const result = await relay.publish(event, { timeoutMs: opts?.timeoutMs });\n return result.ok\n ? { url, status: \"ok\", message: result.message }\n : { url, status: \"rejected\", message: result.message };\n } catch (error) {\n return { url, status: \"failed\", error: toError(error) };\n }\n }),\n );\n }\n\n /** First successful publish (Promise.any semantics). */\n async publishAny(\n relays: ReadonlyArray<string>,\n event: Event,\n opts?: { timeoutMs?: number | undefined },\n ): Promise<Extract<PoolPublishResult, { status: \"ok\" }>> {\n return Promise.any(\n uniqueRelayUrls(relays).map(async (url) => {\n const relay = await this.ensureRelay(url, {\n timeoutMs: this.#relayOptions.connectTimeoutMs,\n });\n this.#touch(relay.url);\n const result = await relay.publish(event, { timeoutMs: opts?.timeoutMs });\n if (!result.ok) {\n throw new RelayPublishError(result.message || \"rejected\", relay.url);\n }\n return { url, status: \"ok\" as const, message: result.message };\n }),\n );\n }\n\n /**\n * NIP-45 COUNT across relays. Per-relay outcomes; failures do not throw. Counts are not summed —\n * each relay reports independently (may overlap).\n */\n async count(\n relays: ReadonlyArray<string>,\n filters: ReadonlyArray<Filter>,\n opts?: { timeoutMs?: number | undefined; signal?: AbortSignal | undefined },\n ): Promise<PoolCountResult[]> {\n throwIfAborted(opts?.signal);\n const canonical = canonicalizeFilters(filters);\n return Promise.all(\n uniqueRelayUrls(relays).map(async (url): Promise<PoolCountResult> => {\n try {\n const relay = await this.ensureRelay(url, {\n signal: opts?.signal,\n timeoutMs: this.#relayOptions.connectTimeoutMs,\n });\n this.#touch(relay.url);\n const payload: CountResult = await relay.count(canonical, {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n });\n return countOk(url, payload);\n } catch (error) {\n // An abort rejects the whole call; per-relay failures are reported.\n if (opts?.signal?.aborted === true) {\n throw abortReason(opts.signal);\n }\n return { url, status: \"failed\", error: toError(error) };\n }\n }),\n );\n }\n\n listRelays(): string[] {\n return [...this.#relays.keys()];\n }\n\n /** Lookup a pooled relay, including disconnected reconnecting entries. */\n getRelay(url: string): Relay | undefined {\n try {\n return this.#relays.get(normalizeURL(url));\n } catch {\n return this.#relays.get(url);\n }\n }\n\n /** Currently connected URLs. Unlike listRelays(), excludes reconnecting/disconnected entries. */\n connectedUrls(): string[] {\n const urls: string[] = [];\n for (const [url, relay] of this.#relays) {\n if (relay.connected) {\n urls.push(url);\n }\n }\n return urls;\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"put-w13ci3sC.mjs","names":[],"sources":["../src/storage/deletion.ts","../src/storage/put.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { eventAddress, parseEventAddress } from \"../core/tag.ts\";\nimport { isHex32 } from \"../core/util.ts\";\n\nexport type DeletionPlan = {\n /** Stored events validated as same-pubkey (not kind 5) and to be removed now. */\n readonly removeIds: ReadonlyArray<string>;\n /** Referenced ids not in the store; remember deletion pubkey until the event arrives. */\n readonly pendingIds: ReadonlyArray<{ readonly id: string; readonly pubkey: string }>;\n /** Replaceable/addressable coordinates tombstoned up to `until` (deletion created_at). */\n readonly coordinates: ReadonlyArray<{ readonly key: string; readonly until: number }>;\n};\n\n/**\n * NIP-09 application plan for a kind 5 event.\n *\n * - `e` tags: delete only when the target exists, is not itself kind 5, and shares pubkey.\n * - Unknown `e` ids are pending until the event arrives (client MUST validate pubkey).\n * - `a` tags: tombstone that coordinate up to this deletion's `created_at` when pubkey matches.\n * - Deleting a deletion request is a no-op.\n */\nexport function planDeletion(\n deletion: Pick<Event, \"pubkey\" | \"created_at\" | \"tags\">,\n getById: (id: string) => Pick<Event, \"id\" | \"pubkey\" | \"kind\"> | undefined,\n): DeletionPlan {\n const { pubkey } = deletion;\n const removeIds: string[] = [];\n const pendingIds: Array<{ id: string; pubkey: string }> = [];\n const coordinates: Array<{ key: string; until: number }> = [];\n const seenIds = new Set<string>();\n const seenCoords = new Set<string>();\n\n for (const tag of deletion.tags) {\n if (tag[0] === \"e\" && tag[1] !== undefined && isHex32(tag[1].toLowerCase())) {\n const id = tag[1].toLowerCase();\n if (seenIds.has(id)) {\n continue;\n }\n seenIds.add(id);\n const existing = getById(id);\n if (!existing) {\n pendingIds.push({ id, pubkey });\n continue;\n }\n if (existing.kind === Kind.EventDeletion) {\n continue;\n }\n if (existing.pubkey !== pubkey) {\n continue;\n }\n removeIds.push(id);\n continue;\n }\n\n if (tag[0] === \"a\" && tag[1] !== undefined) {\n const coord = parseEventAddress(tag[1]);\n if (!coord || coord.pubkey !== pubkey) {\n continue;\n }\n const key = `${coord.kind}:${coord.pubkey}:${coord.identifier}`;\n if (seenCoords.has(key)) {\n continue;\n }\n seenCoords.add(key);\n coordinates.push({ key, until: deletion.created_at });\n }\n }\n\n return { removeIds, pendingIds, coordinates };\n}\n\n/** In-memory NIP-09 tombstones shared by MemoryEventStore and IndexedDbEventStore. */\nexport class DeletionState {\n readonly ids: Set<string> = new Set<string>();\n readonly pending: Map<string, string> = new Map<string, string>();\n readonly coordinates: Map<string, number> = new Map<string, number>();\n\n clear(): void {\n this.ids.clear();\n this.pending.clear();\n this.coordinates.clear();\n }\n\n covers(event: Pick<Event, \"id\" | \"pubkey\" | \"kind\" | \"created_at\" | \"tags\">): boolean {\n if (this.ids.has(event.id)) {\n return true;\n }\n if (event.kind === Kind.EventDeletion) {\n return false;\n }\n const pendingPk = this.pending.get(event.id);\n if (pendingPk !== undefined && event.pubkey === pendingPk) {\n return true;\n }\n const addr = eventAddress(event);\n if (addr === undefined) {\n return false;\n }\n const until = this.coordinates.get(addr);\n return until !== undefined && event.created_at <= until;\n }\n\n absorb(plan: DeletionPlan): void {\n for (const id of plan.removeIds) {\n this.ids.add(id);\n this.pending.delete(id);\n }\n for (const p of plan.pendingIds) {\n if (!this.ids.has(p.id)) {\n this.pending.set(p.id, p.pubkey);\n }\n }\n for (const c of plan.coordinates) {\n const prev = this.coordinates.get(c.key) ?? Number.NEGATIVE_INFINITY;\n // Re-absorbing a coordinate moves it to the newest end so FIFO\n // tombstone caps evict the oldest marker, not the freshest.\n this.coordinates.delete(c.key);\n this.coordinates.set(c.key, Math.max(prev, c.until));\n }\n }\n}\n\nexport function coordinateRemovals(\n coordinates: ReadonlyArray<{ key: string; until: number }>,\n getCurrent: (key: string) => Pick<Event, \"id\" | \"created_at\"> | undefined,\n): string[] {\n const ids: string[] = [];\n for (const c of coordinates) {\n const current = getCurrent(c.key);\n if (current && current.created_at <= c.until) {\n ids.push(current.id);\n }\n }\n return ids;\n}\n","import type { Event } from \"../core/event.ts\";\nimport { isReplaceableWinner, validateSignedEvent } from \"../core/event.ts\";\nimport { isEphemeralKind, Kind } from \"../core/kind.ts\";\nimport { eventAddress } from \"../core/tag.ts\";\nimport { coordinateRemovals, planDeletion } from \"./deletion.ts\";\nimport type { DeletionPlan, DeletionState } from \"./deletion.ts\";\nimport type { PutResult } from \"./types.ts\";\n\nexport function outboxBoundKey(pubkey: string, kind: number): string {\n return `${pubkey.toLowerCase()}:${kind}`;\n}\n\nexport type PutDecision =\n | {\n readonly action: \"skip\";\n readonly result: \"duplicate\" | \"ephemeral\" | \"rejected\" | \"invalid\";\n readonly event: Event;\n }\n | { readonly action: \"tombstone\"; readonly result: \"duplicate\"; readonly event: Event }\n | {\n readonly action: \"delete\";\n readonly result: \"deleted\";\n readonly event: Event;\n readonly plan: DeletionPlan;\n readonly coordIds: ReadonlyArray<string>;\n }\n | {\n readonly action: \"insert\";\n readonly result: \"accepted\" | \"replaced\";\n readonly event: Event;\n readonly address?: string | undefined;\n readonly replaceId?: string | undefined;\n };\n\nexport type PutLookup = {\n deletion: DeletionState;\n getById: (\n id: string,\n ) => Pick<Event, \"id\" | \"pubkey\" | \"kind\" | \"created_at\" | \"tags\"> | undefined;\n /**\n * Current replaceable winner for an address. `evicted: true` marks an eviction watermark: the\n * winning event body was dropped, but its id/created_at still reject older versions, and a re-put\n * of the watermarked id re-inserts it.\n */\n getReplaceable: (\n address: string,\n ) => (Pick<Event, \"id\" | \"created_at\"> & { evicted?: boolean }) | undefined;\n};\n\nexport function decidePut(raw: Event, lookup: PutLookup): PutDecision {\n // Canonical-input precondition: non-canonical events are never indexed.\n if (!validateSignedEvent(raw)) {\n return { action: \"skip\", result: \"invalid\", event: raw };\n }\n const event = raw;\n if (lookup.deletion.ids.has(event.id) || lookup.getById(event.id)) {\n return { action: \"skip\", result: \"duplicate\", event };\n }\n if (event.kind === Kind.EventDeletion) {\n const plan = planDeletion(event, lookup.getById);\n const coordIds = coordinateRemovals(plan.coordinates, lookup.getReplaceable);\n return { action: \"delete\", result: \"deleted\", event, plan, coordIds };\n }\n if (lookup.deletion.covers(event)) {\n return { action: \"tombstone\", result: \"duplicate\", event };\n }\n if (isEphemeralKind(event.kind)) {\n return { action: \"skip\", result: \"ephemeral\", event };\n }\n const address = eventAddress(event);\n if (address !== undefined) {\n const prev = lookup.getReplaceable(address);\n // A stored incumbent rejects stale versions; an eviction watermark does\n // the same, except a re-put of the watermarked id re-inserts it.\n if (prev && prev.id !== event.id && !isReplaceableWinner(event, prev)) {\n return { action: \"skip\", result: \"rejected\", event };\n }\n if (prev !== undefined && prev.evicted !== true) {\n return { action: \"insert\", result: \"replaced\", event, address, replaceId: prev.id };\n }\n return { action: \"insert\", result: \"accepted\", event, address };\n }\n return { action: \"insert\", result: \"accepted\", event };\n}\n\nexport function applyPutMemory(\n s: {\n deletion: DeletionState;\n indexInsert: (event: Event) => void;\n indexRemove: (id: string) => boolean;\n },\n d: PutDecision,\n): PutResult {\n if (d.action === \"skip\") {\n return d.result;\n }\n if (d.action === \"tombstone\") {\n s.deletion.ids.add(d.event.id);\n s.deletion.pending.delete(d.event.id);\n return \"duplicate\";\n }\n if (d.action === \"delete\") {\n s.deletion.pending.delete(d.event.id);\n s.deletion.absorb(d.plan);\n for (const id of d.plan.removeIds) {\n s.indexRemove(id);\n }\n for (const id of d.coordIds) {\n s.deletion.ids.add(id);\n s.indexRemove(id);\n }\n s.indexInsert(d.event);\n return \"deleted\";\n }\n if (d.replaceId !== undefined) {\n s.indexRemove(d.replaceId);\n }\n s.indexInsert(d.event);\n return d.result;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"relay-quCs7MHD.mjs","names":[],"sources":["../src/relay/error.ts","../src/relay/websocket.ts","../src/relay/auth.ts","../src/relay/count-tracker.ts","../src/relay/subscription.ts","../src/relay/fetch.ts","../src/relay/neg-session.ts","../src/relay/ok-tracker.ts","../src/relay/ping.ts","../src/relay/subscription-registry.ts","../src/relay/relay.ts"],"sourcesContent":["import { NostrError } from \"../core/error.ts\";\n\n/** Base class for relay errors; carries the relay URL when known. */\nexport class RelayError extends NostrError {\n override name = \"RelayError\";\n readonly url: string | undefined;\n\n constructor(message: string, url?: string, options?: ErrorOptions) {\n super(url !== undefined && url !== \"\" ? `${message} (${url})` : message, options);\n this.url = url;\n }\n}\n\n/** A relay connection attempt or socket failed. */\nexport class RelayConnectionError extends RelayError {\n override name = \"RelayConnectionError\";\n}\n/** A relay answered an EVENT with `ok: false`. */\nexport class RelayPublishError extends RelayError {\n override name = \"RelayPublishError\";\n}\n/** A subscription was closed by the relay (`CLOSED`) or the socket dropped. */\nexport class RelayClosedError extends RelayError {\n override name = \"RelayClosedError\";\n}\n/** A relay operation exceeded its deadline. */\nexport class RelayTimeoutError extends RelayError {\n override name = \"RelayTimeoutError\";\n}\n/**\n * A relay was suspended for delivering more events that fail id/signature verification than its\n * {@link PoolOptions.invalidEventPolicy} allows. Carries `until`, the epoch-ms time when the relay\n * may connect again.\n */\nexport class RelaySuspendedError extends RelayError {\n override name = \"RelaySuspendedError\";\n /** Epoch milliseconds when the suspension lifts. */\n readonly until: number;\n\n constructor(url: string, until: number) {\n super(\"relay suspended for delivering invalid events\", url);\n this.until = until;\n }\n}\n","import { RelayConnectionError } from \"./error.ts\";\n\n/** Minimal WebSocket surface used by Relay (browser or `ws`). */\nexport type WebSocketLike = {\n readyState: number;\n send: (data: string) => void;\n close: (code?: number, reason?: string) => void;\n addEventListener: (type: string, listener: (ev: unknown) => void) => void;\n removeEventListener: (type: string, listener: (ev: unknown) => void) => void;\n ping?: () => void;\n /** Node `ws` emits `pong` on the EventEmitter; `addEventListener(\"pong\")` is a no-op. */\n once?: (event: string, listener: (...args: unknown[]) => void) => void;\n on?: (event: string, listener: (...args: unknown[]) => void) => void;\n off?: (event: string, listener: (...args: unknown[]) => void) => void;\n};\n\nexport type WebSocketConstructor = {\n new (url: string): WebSocketLike;\n readonly OPEN: number;\n readonly CONNECTING: number;\n readonly CLOSING: number;\n readonly CLOSED: number;\n};\n\nlet impl: WebSocketConstructor | undefined;\n\ntry {\n if (globalThis.WebSocket !== undefined) {\n impl = globalThis.WebSocket;\n }\n} catch {\n // no global WebSocket\n}\n\n/** Inject a WebSocket implementation (required on Node unless undici/global is present). */\nexport function useWebSocketImplementation(websocketImplementation: WebSocketConstructor): void {\n impl = websocketImplementation;\n}\n\nexport function getWebSocketImplementation(): WebSocketConstructor {\n if (!impl) {\n throw new RelayConnectionError(\n \"No WebSocket implementation available; call useWebSocketImplementation() or run in a browser\",\n );\n }\n return impl;\n}\n","/**\n * NIP-42 AUTH state machine: the latest challenge, dedup of signed answers across identical\n * challenges, and the `ensureAuthed` retry loop. AUTH frames are sent through an injected\n * `sendAuth` (OkTracker-backed) so the relay's verdict resolves the auth call.\n */\nimport type { Event, EventTemplate } from \"../core/event.ts\";\nimport { makeAuthEvent } from \"../nips/nip42.ts\";\nimport { NoSignerError } from \"../signer/error.ts\";\nimport { RelayClosedError, RelayError } from \"./error.ts\";\nimport type { PublishResult } from \"./ok-tracker.ts\";\n\nexport type RelayAuthDeps = {\n url: string;\n isOpen: () => boolean;\n /** Send an AUTH frame and await the relay's OK verdict. */\n sendAuth: (event: Event, timeoutMs: number) => Promise<PublishResult>;\n defaultTimeoutMs: number;\n};\n\nexport class RelayAuth {\n #challenge: string | undefined;\n #authedChallenge: string | undefined;\n /** Challenge value already answered on this connection; duplicates are not re-signed. */\n #answeredChallenge: string | undefined;\n /** Settled OK verdict for `#answeredChallenge`; set only once the relay replies. */\n #answeredResult: PublishResult | undefined;\n #authPromise: Promise<PublishResult> | undefined;\n readonly #url: string;\n readonly #isOpen: () => boolean;\n readonly #sendAuth: (event: Event, timeoutMs: number) => Promise<PublishResult>;\n readonly #defaultTimeoutMs: number;\n\n constructor(deps: RelayAuthDeps) {\n this.#url = deps.url;\n this.#isOpen = deps.isOpen;\n this.#sendAuth = deps.sendAuth;\n this.#defaultTimeoutMs = deps.defaultTimeoutMs;\n }\n\n /** Latest NIP-42 challenge, if any. */\n get challenge(): string | undefined {\n return this.#challenge;\n }\n\n /**\n * A re-sent identical challenge keeps the in-flight/settled dedupe; only a new challenge value\n * resets the answer state.\n */\n handleChallenge(challenge: string): void {\n if (challenge !== this.#challenge) {\n this.#authPromise = undefined;\n this.#authedChallenge = undefined;\n this.#answeredChallenge = undefined;\n this.#answeredResult = undefined;\n }\n this.#challenge = challenge;\n }\n\n /** New connection: challenge and answer state belong to the old socket. */\n resetConnection(): void {\n this.#challenge = undefined;\n this.#authPromise = undefined;\n this.#authedChallenge = undefined;\n this.#answeredChallenge = undefined;\n this.#answeredResult = undefined;\n }\n\n /** Sign the current challenge and wait for OK. */\n async auth(\n sign: (template: EventTemplate) => Promise<Event>,\n opts?: { timeoutMs?: number | undefined },\n ): Promise<PublishResult> {\n const challenge = this.#challenge;\n if (challenge === undefined || challenge === \"\") {\n throw new RelayError(\"no AUTH challenge received from relay\", this.#url);\n }\n if (this.#authPromise) {\n return this.#authPromise;\n }\n if (this.#answeredChallenge === challenge && this.#answeredResult !== undefined) {\n return this.#answeredResult;\n }\n\n const pending = (async () => {\n const template = makeAuthEvent(this.#url, challenge);\n let event: Event;\n try {\n event = await sign(template);\n } catch (error) {\n // A lazy signer may legitimately have nothing to sign with; ignore the\n // challenge quietly — the connection stays open without an AUTH frame.\n if (error instanceof NoSignerError) {\n return { ok: false, message: \"auth: no signer\" };\n }\n throw error;\n }\n if (!this.#isOpen()) {\n throw new RelayClosedError(\"not connected\", this.#url);\n }\n const result = await this.#sendAuth(event, opts?.timeoutMs ?? this.#defaultTimeoutMs);\n // Cache the relay's settled verdict: a repeated challenge replays it without\n // re-signing. Timeouts and send failures reject before here, so a later auth()\n // signs again.\n if (this.#challenge === challenge) {\n this.#answeredChallenge = challenge;\n this.#answeredResult = result;\n }\n return result;\n })();\n this.#authPromise = pending;\n try {\n const result = await pending;\n if (result.ok && this.#challenge === challenge) {\n this.#authedChallenge = challenge;\n }\n return result;\n } finally {\n if (this.#authPromise === pending) {\n this.#authPromise = undefined;\n }\n }\n }\n\n async ensureAuthed(\n signer: ((template: EventTemplate) => Promise<Event>) | undefined,\n ): Promise<boolean> {\n if (!signer) {\n return false;\n }\n for (let i = 0; i < 3; i++) {\n if (this.#challenge === undefined || this.#challenge === \"\") {\n return false;\n }\n if (this.#authedChallenge === this.#challenge) {\n return true;\n }\n const signed = this.#challenge;\n // oxlint-disable-next-line no-await-in-loop -- auth retries are sequential: each round waits for the new AUTH challenge\n const result = await this.auth(signer);\n if (this.#authedChallenge === this.#challenge) {\n return true;\n }\n if (!result.ok) {\n if (this.#challenge === undefined || this.#challenge === \"\" || this.#challenge === signed) {\n return false;\n }\n continue;\n }\n }\n return this.#authedChallenge === this.#challenge;\n }\n\n /**\n * Clear a cached AUTH rejection for the current challenge (a successful auth and in-flight\n * answers are kept) and return the unanswered challenge to re-emit, if any.\n */\n resetRejection(): string | undefined {\n if (this.#answeredResult !== undefined && !this.#answeredResult.ok) {\n this.#answeredChallenge = undefined;\n this.#answeredResult = undefined;\n }\n if (this.#challenge !== undefined && this.#challenge !== this.#authedChallenge) {\n return this.#challenge;\n }\n return undefined;\n }\n}\n","/**\n * COUNT request correlation: one waiter per request id with a timeout, abort cleanup and a re-send\n * after AUTH.\n */\nimport { abortReason, onAbort } from \"../core/abort.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport type { CountResult } from \"../core/message.ts\";\n\nexport type CountWaiter = {\n readonly id: string;\n resolve: (result: CountResult) => void;\n reject: (err: unknown) => void;\n timer: ReturnType<typeof setTimeout> | undefined;\n readonly filters: Filter[];\n /** True after one `auth-required:` retry. */\n authRetried: boolean;\n readonly timeoutMs: number;\n readonly timeoutError: () => Error;\n};\n\nexport class CountTracker {\n readonly #entries = new Map<string, CountWaiter>();\n\n get size(): number {\n return this.#entries.size;\n }\n\n get(id: string): CountWaiter | undefined {\n return this.#entries.get(id);\n }\n\n /**\n * Register a waiter, wire the abort listener, arm its timeout and call `send`. A send throw\n * rejects (`sendError` when the throw is not an Error). resolve/reject unwind the timer, abort\n * listener and map entry.\n */\n async track(\n id: string,\n opts: {\n timeoutMs: number;\n timeoutError: () => Error;\n sendError: () => Error;\n filters: Filter[];\n signal?: AbortSignal | undefined;\n send: () => void;\n },\n ): Promise<CountResult> {\n return new Promise<CountResult>((resolve, reject) => {\n const waiter: CountWaiter = {\n id,\n resolve: (result) => {\n this.#finish(waiter);\n cleanup();\n resolve(result);\n },\n reject: (err) => {\n this.#finish(waiter);\n cleanup();\n // oxlint-disable-next-line typescript/prefer-promise-reject-errors -- forwards abort/signal reasons verbatim\n reject(err);\n },\n timer: undefined,\n filters: opts.filters,\n authRetried: false,\n timeoutMs: opts.timeoutMs,\n timeoutError: opts.timeoutError,\n };\n const cleanup = onAbort(opts.signal, () => {\n if (opts.signal !== undefined) {\n waiter.reject(abortReason(opts.signal));\n }\n });\n waiter.timer = setTimeout(() => {\n waiter.reject(opts.timeoutError());\n }, opts.timeoutMs);\n this.#entries.set(id, waiter);\n try {\n opts.send();\n } catch (error) {\n waiter.reject(error instanceof Error ? error : opts.sendError());\n }\n });\n }\n\n /** Stop a waiter's timeout without settling (AUTH retry window). */\n clearTimer(waiter: CountWaiter): void {\n if (waiter.timer !== undefined) {\n clearTimeout(waiter.timer);\n waiter.timer = undefined;\n }\n }\n\n /** Re-arm the timeout for a re-sent COUNT (after an AUTH retry). */\n restartTimer(id: string): void {\n const waiter = this.#entries.get(id);\n if (waiter === undefined) {\n return;\n }\n waiter.timer = setTimeout(() => {\n waiter.reject(waiter.timeoutError());\n }, waiter.timeoutMs);\n }\n\n /**\n * Re-send a COUNT after the relay demanded AUTH: pause the timeout through the AUTH window,\n * re-arm it and re-send on success, reject on failure.\n */\n async authRetry(\n waiter: CountWaiter,\n deps: {\n ensureAuthed: () => Promise<boolean>;\n isOpen: () => boolean;\n failError: () => Error;\n send: () => void;\n },\n ): Promise<void> {\n this.clearTimer(waiter);\n try {\n const ok = await deps.ensureAuthed();\n if (this.#entries.get(waiter.id) !== waiter) {\n return;\n }\n if (!ok || !deps.isOpen()) {\n waiter.reject(deps.failError());\n return;\n }\n this.restartTimer(waiter.id);\n deps.send();\n } catch (error) {\n if (this.#entries.get(waiter.id) === undefined) {\n return;\n }\n waiter.reject(error instanceof Error ? error : deps.failError());\n }\n }\n\n rejectAll(err: Error): void {\n for (const waiter of this.#entries.values()) {\n this.clearTimer(waiter);\n waiter.reject(err);\n }\n this.#entries.clear();\n }\n\n #finish(waiter: CountWaiter): void {\n this.clearTimer(waiter);\n this.#entries.delete(waiter.id);\n }\n}\n","import { onAbort } from \"../core/abort.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { createSubscriptionId } from \"../core/message.ts\";\nimport { invokeSafely } from \"../core/report.ts\";\nimport { RelayClosedError } from \"./error.ts\";\n\nexport type SubscriptionHandlers = {\n onevent?: ((event: Event) => void) | undefined;\n oneose?: (() => void) | undefined;\n onclose?: ((reason: string) => void) | undefined;\n /** Skip verify + onevent when true. Evaluated after parse, before verify. */\n alreadyHaveEvent?: ((id: string) => boolean) | undefined;\n /**\n * Fired for every EVENT id this sub sees, including duplicates and alreadyHaveEvent hits, after\n * parse and before the verify skip.\n */\n receivedEvent?: ((id: string) => void) | undefined;\n};\n\n/** Options for {@link Relay.subscribe}: event handlers plus REQ behavior. */\nexport type SubscribeOptions = SubscriptionHandlers & {\n id?: string | undefined;\n /**\n * If set, fire `oneose` once after this many ms if EOSE has not arrived. The synthesized EOSE\n * also closes a `closeOnEose` subscription — without it a one-shot REQ would hang until a real\n * EOSE that is then ignored.\n */\n eoseTimeoutMs?: number | undefined;\n /**\n * One-shot REQ: do not join a live coalescing group, and close on EOSE. Default false (live).\n * `Relay.fetch` passes true.\n */\n closeOnEose?: boolean | undefined;\n signal?: AbortSignal | undefined;\n};\n\n/** Uniform close handle returned by subscriptions, fan-ins, and feeds. */\nexport type Closer = { close: (reason?: string) => void };\n\n/** Public subscription handle returned by {@link Relay.subscribe}. */\nexport type RelaySubscription = {\n readonly id: string;\n readonly closed: boolean;\n close: (reason?: string) => void;\n};\n\n/**\n * Internal REQ runtime: owns its lifecycle flags, watermark bookkeeping and handler dispatch.\n * `onLocalClose` is supplied by the registry — it sends CLOSE / detaches from a live group — and is\n * invoked by {@link close} only, never by {@link end}.\n */\nexport class Subscription {\n readonly id: string;\n readonly filters: ReadonlyArray<Filter>;\n readonly closeOnEose: boolean;\n readonly #handlers: SubscriptionHandlers;\n readonly #onLocalClose: (sub: Subscription) => void;\n #abortDispose: (() => void) | undefined;\n #eoseTimer: ReturnType<typeof setTimeout> | undefined;\n #closed = false;\n #eosed = false;\n /** True after one CLOSED `auth-required:` retry. */\n #authRetried = false;\n /** Inclusive NIP-01 `since` watermark from verified EVENTs. */\n #lastCreatedAt: number | undefined;\n /** Event ids at `lastCreatedAt` (same-second reconnect dedup). Not all seen ids. */\n readonly #idsAtWatermark = new Set<string>();\n\n constructor(\n filters: ReadonlyArray<Filter>,\n opts: SubscribeOptions,\n onLocalClose: (sub: Subscription) => void,\n ) {\n this.#onLocalClose = onLocalClose;\n this.id = createSubscriptionId(opts.id);\n this.filters = filters;\n this.closeOnEose = opts.closeOnEose === true;\n this.#handlers = {\n onevent: opts.onevent,\n oneose: opts.oneose,\n onclose: opts.onclose,\n alreadyHaveEvent: opts.alreadyHaveEvent,\n receivedEvent: opts.receivedEvent,\n };\n if (opts.eoseTimeoutMs !== undefined) {\n this.#eoseTimer = setTimeout(() => {\n this.#eoseTimer = undefined;\n this.markEose();\n }, opts.eoseTimeoutMs);\n }\n if (opts.signal) {\n if (opts.signal.aborted) {\n this.close(\"aborted\");\n } else {\n this.#abortDispose = onAbort(opts.signal, () => this.close(\"aborted\"));\n }\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n\n get eosed(): boolean {\n return this.#eosed;\n }\n\n get lastCreatedAt(): number | undefined {\n return this.#lastCreatedAt;\n }\n\n get idsAtWatermark(): ReadonlySet<string> {\n return this.#idsAtWatermark;\n }\n\n /**\n * Local close: idempotent; clears the EOSE timer and abort listener, detaches via `onLocalClose`\n * (sends CLOSE / leaves the live group), then fires `onclose`.\n */\n close(reason = \"closed by client\"): void {\n if (this.#closed) {\n return;\n }\n this.#closed = true;\n this.#teardown();\n this.#onLocalClose(this);\n invokeSafely(() => this.#handlers.onclose?.(reason));\n }\n\n /** Remote/transport termination: like {@link close} but `onLocalClose` is not called. */\n end(reason: string): void {\n if (this.#closed) {\n return;\n }\n this.#closed = true;\n this.#teardown();\n invokeSafely(() => this.#handlers.onclose?.(reason));\n }\n\n #teardown(): void {\n if (this.#eoseTimer !== undefined) {\n clearTimeout(this.#eoseTimer);\n this.#eoseTimer = undefined;\n }\n this.#abortDispose?.();\n this.#abortDispose = undefined;\n }\n\n /** Mark EOSE: fires `oneose`, then closes when `closeOnEose`. No-op when closed or EOSE'd. */\n markEose(): void {\n if (this.#closed || this.#eosed) {\n return;\n }\n this.#eosed = true;\n if (this.#eoseTimer !== undefined) {\n clearTimeout(this.#eoseTimer);\n this.#eoseTimer = undefined;\n }\n invokeSafely(() => this.#handlers.oneose?.());\n if (this.closeOnEose) {\n this.close(\"eose\");\n }\n }\n\n /** Clear `eosed` for a replayed REQ (AUTH retry / reconnect). */\n rearm(): void {\n this.#eosed = false;\n }\n\n /** First `auth-required:` retry wins; later calls report false without side effects. */\n beginAuthRetry(): boolean {\n if (this.#authRetried) {\n return false;\n }\n this.#authRetried = true;\n return true;\n }\n\n resetAuthRetry(): void {\n this.#authRetried = false;\n }\n\n /** `receivedEvent` for every EVENT id, before watermark/alreadyHaveEvent/verify. */\n notifyReceived(id: string): void {\n invokeSafely(() => this.#handlers.receivedEvent?.(id));\n }\n\n alreadyHas(id: string): boolean {\n let have = false;\n invokeSafely(() => {\n have = Boolean(this.#handlers.alreadyHaveEvent?.(id));\n });\n return have;\n }\n\n isAtWatermark(id: string): boolean {\n return this.#idsAtWatermark.has(id);\n }\n\n /** Verified event: advance the watermark, then fire `onevent`. */\n deliver(event: Event): void {\n this.noteVerified(event);\n invokeSafely(() => this.#handlers.onevent?.(event));\n }\n\n /** Advance the reconnect watermark after a verified EVENT. */\n noteVerified(event: Event): void {\n if (this.#lastCreatedAt === undefined || event.created_at > this.#lastCreatedAt) {\n this.#lastCreatedAt = event.created_at;\n this.#idsAtWatermark.clear();\n this.#idsAtWatermark.add(event.id);\n return;\n }\n if (event.created_at === this.#lastCreatedAt) {\n this.#idsAtWatermark.add(event.id);\n }\n }\n\n /**\n * Filters for re-REQ. Original `filters` stay unchanged. NIP-01 `since` is inclusive — never\n * `lastCreatedAt + 1`.\n */\n replayFilters(): Filter[] {\n const since = this.#lastCreatedAt;\n if (since === undefined) {\n return [...this.filters];\n }\n return this.filters.map((f) => ({\n ...f,\n since: f.since === undefined ? since : Math.max(f.since, since),\n }));\n }\n}\n\n/** AsyncIterable wrapper over a subscription's events until EOSE or close. */\nexport function subscriptionToAsyncIterable(\n start: (handlers: SubscriptionHandlers) => Closer,\n opts?: { signal?: AbortSignal | undefined; includeEose?: boolean | undefined },\n): AsyncIterable<Event> & Closer {\n const queue: Event[] = [];\n let head = 0;\n let done = false;\n let error: Error | undefined;\n let wake: (() => void) | undefined;\n // `let` is required: onclose can fire synchronously while start() is still assigning closer.\n // oxlint-disable-next-line prefer-const\n let closer: Closer | undefined;\n // Set before every locally initiated close so onclose can distinguish it\n // from a remote/transport close without comparing reason strings.\n let localClose = false;\n let disposeAbort: (() => void) | undefined;\n\n const notify = () => {\n wake?.();\n wake = undefined;\n };\n\n const waitForWake = async (): Promise<void> =>\n new Promise<void>((resolve) => {\n wake = resolve;\n });\n\n const closeLocal = (reason: string): void => {\n localClose = true;\n disposeAbort?.();\n closer?.close(reason);\n done = true;\n notify();\n };\n\n closer = start({\n onevent(event) {\n queue.push(event);\n notify();\n },\n oneose() {\n if (opts?.includeEose === false) {\n closeLocal(\"eose\");\n }\n },\n onclose(reason) {\n // The Subscription's own abort listener fires before this wrapper's,\n // so a signal-driven close can surface here while localClose is still\n // false — it is still a local close, not a remote one.\n if (opts?.signal?.aborted === true) {\n localClose = true;\n }\n if (!localClose && reason) {\n error = new RelayClosedError(reason);\n }\n done = true;\n notify();\n },\n });\n\n if (opts?.signal) {\n if (opts.signal.aborted) {\n closeLocal(\"aborted\");\n } else {\n disposeAbort = onAbort(opts.signal, () => closeLocal(\"aborted\"));\n // `start` may have ended the subscription synchronously before the\n // listener was registered.\n if (done) {\n disposeAbort();\n }\n }\n }\n\n return {\n close(reason?: string) {\n closeLocal(reason ?? \"closed by client\");\n },\n [Symbol.asyncIterator]() {\n return {\n async next(): Promise<IteratorResult<Event>> {\n while (true) {\n const value = head < queue.length ? queue[head] : undefined;\n if (value !== undefined) {\n head += 1;\n if (head === queue.length) {\n queue.length = 0;\n head = 0;\n }\n return { value, done: false };\n }\n if (error !== undefined) {\n throw error;\n }\n if (done) {\n return { value: undefined, done: true };\n }\n // oxlint-disable-next-line no-await-in-loop -- the async iterator waits for each EVENT serially\n await waitForWake();\n }\n },\n async return(): Promise<IteratorResult<Event>> {\n closeLocal(\"iterator returned\");\n return { value: undefined, done: true };\n },\n };\n },\n };\n}\n","import { abortReason, onAbort, throwIfAborted } from \"../core/abort.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { subscriptionToAsyncIterable } from \"./subscription.ts\";\nimport type { Closer, RelaySubscription, SubscribeOptions } from \"./subscription.ts\";\n\n/** How a one-shot {@link Relay.fetch} REQ ended. */\nexport type RelayFetchEnd =\n | { readonly type: \"eose\" }\n | { readonly type: \"closed\"; readonly reason: string }\n | { readonly type: \"timeout\" };\n\n/** `Relay.fetch` result: collected events plus the REQ's end reason. */\nexport type RelayFetchResult = {\n readonly events: ReadonlyArray<Event>;\n readonly end: RelayFetchEnd;\n};\n\nexport function streamFilters(\n subscribe: (filters: ReadonlyArray<Filter>, opts: SubscribeOptions) => RelaySubscription,\n filters: ReadonlyArray<Filter>,\n opts?: { signal?: AbortSignal | undefined; id?: string | undefined },\n): AsyncIterable<Event> & Closer {\n return subscriptionToAsyncIterable(\n (handlers) => subscribe(filters, { ...handlers, id: opts?.id, signal: opts?.signal }),\n { signal: opts?.signal },\n );\n}\n\nexport async function fetchFilters(\n subscribe: (filters: ReadonlyArray<Filter>, opts: SubscribeOptions) => RelaySubscription,\n filters: ReadonlyArray<Filter>,\n opts: {\n timeoutMs: number;\n signal?: AbortSignal | undefined;\n id?: string | undefined;\n },\n): Promise<RelayFetchResult> {\n throwIfAborted(opts.signal);\n const events: Event[] = [];\n const seen = new Set<string>();\n let end: RelayFetchEnd = { type: \"timeout\" };\n\n await new Promise<void>((resolve, reject) => {\n let settled = false;\n const done = (err?: unknown) => {\n if (settled) {\n return;\n }\n settled = true;\n disposeAbort();\n clearTimeout(timer);\n let reason = \"aborted\";\n if (err instanceof Error) {\n reason = err.message;\n } else if (err === undefined) {\n reason = \"fetch complete\";\n }\n sub.close(reason);\n if (err === undefined) {\n resolve();\n } else {\n // oxlint-disable-next-line typescript/prefer-promise-reject-errors -- forwards abort/signal reasons verbatim\n reject(err);\n }\n };\n\n const disposeAbort = onAbort(opts.signal, () => {\n if (opts.signal !== undefined) {\n done(abortReason(opts.signal));\n }\n });\n\n const timer = setTimeout(() => {\n end = { type: \"timeout\" };\n done();\n }, opts.timeoutMs);\n\n const sub = subscribe(filters, {\n id: opts.id,\n signal: opts.signal,\n closeOnEose: true,\n onevent(event) {\n if (seen.has(event.id)) {\n return;\n }\n seen.add(event.id);\n events.push(event);\n },\n oneose() {\n end = { type: \"eose\" };\n done();\n },\n onclose(reason) {\n // The Subscription's own abort listener may close it before ours runs;\n // an aborted signal still rejects the fetch with the signal's reason.\n if (settled) {\n return;\n }\n if (opts.signal?.aborted === true) {\n done(abortReason(opts.signal));\n return;\n }\n // A remote CLOSED (or transport drop) still returns collected events.\n end = { type: \"closed\", reason };\n done();\n },\n });\n });\n\n return { events, end };\n}\n","import { abortReason, onAbort } from \"../core/abort.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport type { ClientMessage } from \"../core/message.ts\";\nimport { Nip77Error, runNegSession } from \"../nips/nip77.ts\";\nimport type { NegentropyStorageVector } from \"../nips/nip77.ts\";\nimport { RelayTimeoutError } from \"./error.ts\";\n\nexport type NegSession = {\n queue: string[];\n waiter:\n | {\n resolve: (hex: string) => void;\n reject: (err: unknown) => void;\n }\n | undefined;\n error: Error | undefined;\n};\n\nexport function createNegSession(): NegSession {\n return { queue: [], waiter: undefined, error: undefined };\n}\n\nexport function pushNegMsg(session: NegSession, hex: string): void {\n if (session.waiter) {\n const { waiter } = session;\n session.waiter = undefined;\n waiter.resolve(hex);\n } else {\n session.queue.push(hex);\n }\n}\n\nexport function failNegSession(session: NegSession, err: Error): void {\n session.error = err;\n if (session.waiter) {\n const { waiter } = session;\n session.waiter = undefined;\n waiter.reject(err);\n }\n}\n\nexport function failNegErr(session: NegSession, reason: string): void {\n failNegSession(session, new Nip77Error(reason));\n}\n\n/** Route a NEG-MSG/NEG-ERR frame to its session; unknown ids are ignored. */\nexport function dispatchNegMessage(\n sessions: Map<string, NegSession>,\n msg: readonly [type: string, id: string, payload: string],\n): void {\n const session = sessions.get(msg[1]);\n if (session === undefined) {\n return;\n }\n if (msg[0] === \"NEG-MSG\") {\n pushNegMsg(session, msg[2]);\n return;\n }\n failNegErr(session, msg[2]);\n}\n\n/** Fail every tracked session (socket teardown). */\nexport function failAllNegSessions(sessions: Map<string, NegSession>, err: Error): void {\n for (const session of sessions.values()) {\n failNegSession(session, err);\n }\n sessions.clear();\n}\n\n/** Wire queue/timeout/abort around `runNegSession`. Not a second session class. */\nexport async function runWiredNegSession(opts: {\n session: NegSession;\n storage: NegentropyStorageVector;\n filter: Filter;\n id: string;\n timeoutMs: number;\n signal?: AbortSignal | undefined;\n send: (message: ClientMessage) => void;\n url: string;\n}): Promise<{ have: string[]; need: string[] }> {\n const { session, storage, filter, id, timeoutMs, signal, send, url } = opts;\n const deadline = Date.now() + timeoutMs;\n\n const timedOut = (): RelayTimeoutError => new RelayTimeoutError(\"negentropy timed out\", url);\n\n const remainingMs = (): number => deadline - Date.now();\n\n const next = async (): Promise<string> => {\n if (session.error !== undefined) {\n throw session.error;\n }\n if (remainingMs() <= 0) {\n throw timedOut();\n }\n const queued = session.queue.shift();\n if (queued !== undefined) {\n return queued;\n }\n return new Promise<string>((resolve, reject) => {\n const timer = setTimeout(() => {\n session.waiter = undefined;\n disposeAbort();\n reject(timedOut());\n }, remainingMs());\n const fail = (err: unknown): void => {\n clearTimeout(timer);\n session.waiter = undefined;\n disposeAbort();\n // oxlint-disable-next-line typescript/prefer-promise-reject-errors -- forwards abort/signal reasons verbatim\n reject(err);\n };\n session.waiter = {\n resolve: (hex) => {\n clearTimeout(timer);\n disposeAbort();\n resolve(hex);\n },\n reject: (err) => fail(err),\n };\n const disposeAbort = onAbort(signal, () => {\n if (signal) {\n fail(abortReason(signal));\n }\n });\n });\n };\n\n return runNegSession({\n storage,\n openingSend: (hex) => {\n send([\"NEG-OPEN\", id, filter, hex]);\n },\n msgSend: (hex) => {\n send([\"NEG-MSG\", id, hex]);\n },\n next,\n });\n}\n\n/**\n * Drive a NIP-77 reconcile session against the session map: fail a same-id predecessor, register,\n * wire queue/timeout/abort, and send NEG-CLOSE on exit.\n */\nexport async function runTrackedNegSession(opts: {\n sessions: Map<string, NegSession>;\n id: string;\n storage: NegentropyStorageVector;\n filter: Filter;\n timeoutMs: number;\n signal?: AbortSignal | undefined;\n send: (message: ClientMessage) => void;\n url: string;\n}): Promise<{ have: string[]; need: string[] }> {\n const { sessions, id, send } = opts;\n const prev = sessions.get(id);\n if (prev) {\n failNegSession(prev, new Nip77Error(\"closed: replaced by new NEG-OPEN\"));\n }\n const session = createNegSession();\n sessions.set(id, session);\n try {\n return await runWiredNegSession({\n session,\n storage: opts.storage,\n filter: opts.filter,\n id,\n timeoutMs: opts.timeoutMs,\n signal: opts.signal,\n send,\n url: opts.url,\n });\n } finally {\n if (sessions.get(id) === session) {\n sessions.delete(id);\n try {\n send([\"NEG-CLOSE\", id]);\n } catch {\n // connection already gone\n }\n }\n }\n}\n","/** OK correlation for EVENT and AUTH frames: one waiter per event id with a timeout. */\nimport type { Event } from \"../core/event.ts\";\n\n/** A relay's NIP-01 OK reply to a published event. */\nexport type PublishResult = {\n readonly ok: boolean;\n readonly message: string;\n};\n\nexport type OkWaiter = {\n readonly id: string;\n resolve: (result: PublishResult) => void;\n reject: (err: Error) => void;\n timer: ReturnType<typeof setTimeout> | undefined;\n /** Set for `publish` waiters; absent for AUTH waiters. Also marks joinable entries. */\n event: Event | undefined;\n /** True after one `auth-required:` retry. */\n authRetried: boolean;\n readonly timeoutMs: number;\n readonly timeoutError: () => Error;\n /** The owning publish()/auth() promise — joiners on the same event id settle with it. */\n readonly promise: Promise<PublishResult>;\n};\n\nexport class OkTracker {\n readonly #entries = new Map<string, OkWaiter>();\n\n get size(): number {\n return this.#entries.size;\n }\n\n get(eventId: string): OkWaiter | undefined {\n return this.#entries.get(eventId);\n }\n\n /** Joiner's view: only publish waiters (carrying an `event`) are joinable. */\n inFlight(eventId: string): Promise<PublishResult> | undefined {\n const waiter = this.#entries.get(eventId);\n return waiter?.event === undefined ? undefined : waiter.promise;\n }\n\n /**\n * Register a waiter, arm its timeout and call `send`. A send throw removes the entry and rejects\n * (`sendError` when the throw is not an Error). The timer only rejects while this waiter still\n * owns the id.\n */\n async track(\n eventId: string,\n opts: {\n timeoutMs: number;\n timeoutError: () => Error;\n sendError: () => Error;\n event?: Event;\n send: () => void;\n },\n ): Promise<PublishResult> {\n const promise = new Promise<PublishResult>((resolve, reject) => {\n const waiter: OkWaiter = {\n id: eventId,\n resolve,\n reject,\n timer: setTimeout(() => {\n if (this.#entries.get(eventId) === waiter) {\n this.#entries.delete(eventId);\n reject(opts.timeoutError());\n }\n }, opts.timeoutMs),\n event: opts.event,\n authRetried: false,\n timeoutMs: opts.timeoutMs,\n timeoutError: opts.timeoutError,\n get promise() {\n return promise;\n },\n };\n this.#entries.set(eventId, waiter);\n try {\n opts.send();\n } catch (error) {\n this.#finish(waiter);\n reject(error instanceof Error ? error : opts.sendError());\n }\n });\n return promise;\n }\n\n /**\n * An OK reply arrived: `retry` is the caller's auth-required policy — a kept waiter is returned\n * unsettled for the retry path; otherwise the entry is settled and undefined is returned.\n */\n handleOk(\n eventId: string,\n ok: boolean,\n message: string,\n retry: (waiter: OkWaiter) => boolean,\n ): OkWaiter | undefined {\n const waiter = this.#entries.get(eventId);\n if (waiter === undefined) {\n return undefined;\n }\n if (!retry(waiter)) {\n this.settle(waiter, { ok, message });\n return undefined;\n }\n return waiter;\n }\n\n /** Settle a waiter: stop its timer, drop the entry it still owns, then resolve. */\n settle(waiter: OkWaiter, result: PublishResult): void {\n this.#finish(waiter);\n waiter.resolve(result);\n }\n\n /** Stop a waiter's timeout without settling (AUTH retry window). */\n clearTimer(waiter: OkWaiter): void {\n if (waiter.timer !== undefined) {\n clearTimeout(waiter.timer);\n waiter.timer = undefined;\n }\n }\n\n /** Re-arm the timeout for a re-sent frame (after an AUTH retry). */\n restartTimer(eventId: string): void {\n const waiter = this.#entries.get(eventId);\n if (waiter === undefined) {\n return;\n }\n waiter.timer = setTimeout(() => {\n if (this.#entries.get(eventId) === waiter) {\n this.#entries.delete(eventId);\n waiter.reject(waiter.timeoutError());\n }\n }, waiter.timeoutMs);\n }\n\n /**\n * Re-send a tracked EVENT after the relay demanded AUTH: pause the timeout through the AUTH\n * window, re-arm it and re-send on success, settle `{ok:false,message}` on failure. AUTH waiters\n * (no `event`) settle immediately.\n */\n async authRetry(\n waiter: OkWaiter,\n message: string,\n deps: {\n ensureAuthed: () => Promise<boolean>;\n isOpen: () => boolean;\n send: (event: Event) => void;\n },\n ): Promise<void> {\n this.clearTimer(waiter);\n const finish = (result: PublishResult) => {\n this.settle(waiter, result);\n };\n try {\n const { event } = waiter;\n if (event === undefined) {\n finish({ ok: false, message });\n return;\n }\n const ok = await deps.ensureAuthed();\n if (this.#entries.get(waiter.id) !== waiter) {\n return;\n }\n if (!ok || !deps.isOpen()) {\n finish({ ok: false, message });\n return;\n }\n this.restartTimer(waiter.id);\n deps.send(event);\n } catch {\n if (this.#entries.get(waiter.id) === undefined) {\n return;\n }\n finish({ ok: false, message });\n }\n }\n\n rejectAll(err: Error): void {\n for (const waiter of this.#entries.values()) {\n this.clearTimer(waiter);\n waiter.reject(err);\n }\n this.#entries.clear();\n }\n\n #finish(waiter: OkWaiter): void {\n this.clearTimer(waiter);\n if (this.#entries.get(waiter.id) === waiter) {\n this.#entries.delete(waiter.id);\n }\n }\n}\n","import type { Filter } from \"../core/filter.ts\";\nimport type { ClientMessage, SubscriptionId } from \"../core/message.ts\";\nimport type { WebSocketLike } from \"./websocket.ts\";\n\nexport const DEFAULT_PING_INTERVAL_MS = 29_000;\nexport const DEFAULT_PING_TIMEOUT_MS = 20_000;\n\nconst PING_DUMMY_ID = \"a\".repeat(64);\nconst PING_DUMMY_FILTER: Filter = { ids: [PING_DUMMY_ID], limit: 0 };\n/** Browser WebSocket and `ws` both use 1 for OPEN. */\nconst WS_OPEN = 1;\n\ntype PingWaiter = {\n resolve: (alive: boolean) => void;\n};\n\nexport type PingLoopOpts = {\n send: (message: ClientMessage) => void;\n nextSubId: (prefix: string) => string;\n getWs: () => WebSocketLike | undefined;\n closeWs: () => void;\n pingIntervalMs: number;\n pingTimeoutMs: number;\n};\n\n/** Node `ws` delivers `pong` on the EventEmitter, not via addEventListener. */\nexport function canNativePing(ws: WebSocketLike): boolean {\n return (\n typeof ws.ping === \"function\" && (typeof ws.once === \"function\" || typeof ws.on === \"function\")\n );\n}\n\n/** REQ-dummy or native ping/pong keepalive. Does not import class Relay. */\nexport class PingLoop {\n readonly #opts: PingLoopOpts;\n #timer: ReturnType<typeof setInterval> | undefined;\n readonly #waiters = new Map<SubscriptionId, PingWaiter>();\n #gen = 0;\n #nativePing: { abort: () => void } | undefined;\n\n constructor(opts: PingLoopOpts) {\n this.#opts = opts;\n }\n\n start(): void {\n this.stop();\n this.#timer = setInterval(() => {\n void this.#pingpong();\n }, this.#opts.pingIntervalMs);\n }\n\n stop(): void {\n if (this.#timer !== undefined) {\n clearInterval(this.#timer);\n this.#timer = undefined;\n }\n this.#abortCurrentPing();\n }\n\n hasWaiter(id: string): boolean {\n return this.#waiters.has(id);\n }\n\n finishDummyPing(id: string): boolean {\n const waiter = this.#waiters.get(id);\n if (!waiter) {\n return false;\n }\n this.#waiters.delete(id);\n waiter.resolve(true);\n try {\n this.#opts.send([\"CLOSE\", id]);\n } catch {\n // socket already gone\n }\n return true;\n }\n\n #abortCurrentPing(): void {\n this.#gen += 1;\n const native = this.#nativePing;\n this.#nativePing = undefined;\n native?.abort();\n for (const waiter of this.#waiters.values()) {\n waiter.resolve(false);\n }\n this.#waiters.clear();\n }\n\n async #pingpong(): Promise<void> {\n const ws = this.#opts.getWs();\n if (!ws || ws.readyState !== WS_OPEN) {\n return;\n }\n if (this.#nativePing !== undefined || this.#waiters.size > 0) {\n return;\n }\n\n const gen = this.#gen;\n const ping = canNativePing(ws) ? this.#waitForNativePing(ws) : this.#waitForDummyPing();\n const ok = await new Promise<boolean>((resolve) => {\n let settled = false;\n const done = (alive: boolean) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n resolve(alive);\n };\n const timer = setTimeout(() => {\n if (this.#nativePing !== undefined || this.#waiters.size > 0) {\n done(false);\n }\n }, this.#opts.pingTimeoutMs);\n void (async (): Promise<void> => {\n done(await ping);\n })();\n });\n\n if (gen !== this.#gen) {\n return;\n }\n\n if (!ok) {\n this.#abortCurrentPing();\n this.#opts.closeWs();\n }\n }\n\n async #waitForNativePing(ws: WebSocketLike): Promise<boolean> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = (alive: boolean) => {\n if (settled) {\n return;\n }\n settled = true;\n this.#nativePing = undefined;\n ws.off?.(\"pong\", onPong);\n resolve(alive);\n };\n const onPong = () => finish(true);\n this.#nativePing = { abort: () => finish(false) };\n\n // node `ws` once() wraps the listener; off(fn) would miss it.\n if (typeof ws.on === \"function\" && typeof ws.off === \"function\") {\n ws.on(\"pong\", onPong);\n } else if (typeof ws.once === \"function\") {\n ws.once(\"pong\", onPong);\n } else {\n ws.on?.(\"pong\", onPong);\n }\n try {\n if (typeof ws.ping === \"function\") {\n ws.ping();\n } else {\n finish(false);\n }\n } catch {\n finish(false);\n }\n });\n }\n\n async #waitForDummyPing(): Promise<boolean> {\n return new Promise((resolve) => {\n const id = this.#opts.nextSubId(\"__ping__\");\n this.#waiters.set(id, { resolve });\n try {\n this.#opts.send([\"REQ\", id, PING_DUMMY_FILTER]);\n } catch {\n this.#waiters.delete(id);\n resolve(false);\n }\n });\n }\n}\n","/** REQ subscription runtime: exclusive one-shot, live coalescing, dispatch, reconnect replay. */\nimport type { Event } from \"../core/event.ts\";\nimport { filterFingerprint } from \"../core/filter.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport type { ClientMessage, SubscriptionId } from \"../core/message.ts\";\nimport { invokeSafely } from \"../core/report.ts\";\nimport type { EventVerifier } from \"../core/verifier.ts\";\nimport { Subscription } from \"./subscription.ts\";\nimport type { SubscribeOptions } from \"./subscription.ts\";\n\nexport type LiveGroup = {\n fp: string;\n sub: Subscription;\n attachments: Set<Subscription>;\n};\n\ntype RegistryDeps = {\n isOpen: () => boolean;\n enableReconnect: boolean;\n send: (message: ClientMessage) => void;\n scheduleReconnect: () => void;\n acceptEvent: EventVerifier;\n};\n\n/**\n * Owns every open REQ of a relay: exclusive subscriptions keyed by id, and live coalescing groups\n * (one wire REQ plus per-subscriber handles) keyed by fingerprint and wire id.\n */\nexport class SubscriptionRegistry {\n readonly #subs = new Map<SubscriptionId, Subscription>();\n readonly #liveByFp = new Map<string, LiveGroup>();\n readonly #liveBySubId = new Map<SubscriptionId, LiveGroup>();\n readonly #isOpen: () => boolean;\n readonly #enableReconnect: boolean;\n readonly #send: (message: ClientMessage) => void;\n readonly #scheduleReconnect: () => void;\n readonly #acceptEvent: EventVerifier;\n\n constructor(deps: RegistryDeps) {\n this.#isOpen = deps.isOpen;\n this.#enableReconnect = deps.enableReconnect;\n this.#send = deps.send;\n this.#scheduleReconnect = deps.scheduleReconnect;\n this.#acceptEvent = deps.acceptEvent;\n }\n\n /** User subscriptions only; dummy ping REQs are not counted. */\n get size(): number {\n return this.#subs.size;\n }\n\n get(subId: SubscriptionId): Subscription | undefined {\n return this.#subs.get(subId);\n }\n\n /** Exclusive one-shot REQ when `closeOnEose`, live coalescing otherwise. */\n open(filters: ReadonlyArray<Filter>, opts: SubscribeOptions): Subscription {\n if (opts.closeOnEose === true) {\n return this.#openExclusive(filters, opts);\n }\n return this.#openLive(filters, opts);\n }\n\n handleEvent(subId: SubscriptionId, event: Event): void {\n const sub = this.#subs.get(subId);\n if (!sub || sub.closed) {\n return;\n }\n const group = this.#liveBySubId.get(subId);\n if (group) {\n this.#deliverLiveEvent(group, event);\n return;\n }\n sub.notifyReceived(event.id);\n if (sub.isAtWatermark(event.id)) {\n return;\n }\n if (sub.alreadyHas(event.id)) {\n return;\n }\n if (!this.#acceptEvent(event)) {\n return;\n }\n sub.deliver(event);\n }\n\n handleEose(subId: SubscriptionId): void {\n const sub = this.#subs.get(subId);\n if (!sub || sub.closed || sub.eosed) {\n return;\n }\n const group = this.#liveBySubId.get(subId);\n if (group) {\n sub.markEose();\n this.#deliverLiveEose(group);\n return;\n }\n sub.markEose();\n }\n\n /** Remote/transport termination of a REQ (CLOSED, auth-retry failure). */\n drop(sub: Subscription, reason: string): void {\n const group = this.#liveBySubId.get(sub.id);\n if (group) {\n this.#endLiveGroup(group, { sendClose: false, reason });\n return;\n }\n this.#subs.delete(sub.id);\n sub.end(reason);\n }\n\n /**\n * Re-REQ after the relay demanded AUTH: drop the subscription when auth fails or the\n * socket/subscription died during the AUTH window, replay the REQ on success.\n */\n async authRetry(\n sub: Subscription,\n reason: string,\n ensureAuthed: () => Promise<boolean>,\n ): Promise<void> {\n try {\n if (!(await ensureAuthed()) || sub.closed || !this.#isOpen()) {\n this.drop(sub, reason);\n return;\n }\n this.replay(sub);\n } catch {\n this.drop(sub, reason);\n }\n }\n\n /** Re-REQ after AUTH: rearm the wire and its handles, then send the replayed filters. */\n replay(sub: Subscription): void {\n sub.rearm();\n const group = this.#liveBySubId.get(sub.id);\n if (group) {\n for (const att of group.attachments) {\n att.rearm();\n }\n }\n this.#send([\"REQ\", sub.id, ...sub.replayFilters()]);\n }\n\n /** Reconnect path: rearm every open REQ and re-send; false on send failure. */\n replayAll(): boolean {\n for (const sub of this.#subs.values()) {\n if (sub.closed) {\n continue;\n }\n sub.rearm();\n sub.resetAuthRetry();\n const group = this.#liveBySubId.get(sub.id);\n if (group) {\n for (const att of group.attachments) {\n att.rearm();\n att.resetAuthRetry();\n }\n }\n try {\n this.#send([\"REQ\", sub.id, ...sub.replayFilters()]);\n } catch {\n return false;\n }\n }\n return true;\n }\n\n closeAll(reason: string): void {\n const groups = [...this.#liveBySubId.values()];\n for (const group of groups) {\n invokeSafely(() => {\n this.#endLiveGroup(group, { sendClose: false, reason });\n });\n }\n for (const sub of this.#subs.values()) {\n sub.end(reason);\n }\n this.#subs.clear();\n this.#liveByFp.clear();\n this.#liveBySubId.clear();\n }\n\n #openExclusive(filters: ReadonlyArray<Filter>, opts: SubscribeOptions): Subscription {\n const sub = new Subscription(filters, opts, (closed) => {\n this.#subs.delete(closed.id);\n try {\n if (this.#isOpen()) {\n this.#send([\"CLOSE\", closed.id]);\n }\n } catch {\n // ignore\n }\n });\n\n this.#subs.set(sub.id, sub);\n if (this.#isOpen()) {\n this.#send([\"REQ\", sub.id, ...filters]);\n } else if (this.#enableReconnect) {\n this.#scheduleReconnect();\n }\n return sub;\n }\n\n #openLive(filters: ReadonlyArray<Filter>, opts: SubscribeOptions): Subscription {\n const fp = filterFingerprint(filters);\n // Coalesce only into a wire that has not EOSE'd yet: a late subscriber on an\n // EOSE'd wire would see an empty synthesized EOSE and no replay, so it gets a\n // fresh REQ of its own instead. `liveByFp` always points at the joinable wire.\n let group = this.#liveByFp.get(fp);\n if (group?.sub.eosed === true) {\n group = undefined;\n }\n let created = false;\n if (!group) {\n created = true;\n const slot: { group: LiveGroup | undefined } = { group: undefined };\n const wire = new Subscription(filters, { id: opts.id }, () => {\n const g = slot.group;\n if (g) {\n this.#endLiveGroup(g, { sendClose: true, reason: \"closed by client\" });\n }\n });\n group = { fp, sub: wire, attachments: new Set() };\n slot.group = group;\n this.#liveByFp.set(fp, group);\n this.#liveBySubId.set(wire.id, group);\n this.#subs.set(wire.id, wire);\n }\n\n const slot: { handle: Subscription | undefined } = { handle: undefined };\n // The handle detaches from its own group, never looked up by fingerprint:\n // `liveByFp` may already point at a newer wire for the same fingerprint.\n const attached = group;\n const handle = new Subscription(filters, { ...opts, id: group.sub.id }, () => {\n // constructor may close on an already-aborted signal before `handle` is assigned\n const current = slot.handle;\n if (!current) {\n return;\n }\n this.#detachLive(attached, current);\n });\n slot.handle = handle;\n\n if (handle.closed) {\n if (created && group.attachments.size === 0) {\n this.#forgetLiveGroup(group, \"closed by client\");\n }\n return handle;\n }\n\n group.attachments.add(handle);\n\n if (created) {\n if (this.#isOpen()) {\n this.#send([\"REQ\", group.sub.id, ...filters]);\n } else if (this.#enableReconnect) {\n this.#scheduleReconnect();\n }\n }\n\n return handle;\n }\n\n #forgetLiveGroup(group: LiveGroup, reason: string): void {\n // A newer wire may already own the fingerprint slot; only unlink this group.\n if (this.#liveByFp.get(group.fp) === group) {\n this.#liveByFp.delete(group.fp);\n }\n this.#liveBySubId.delete(group.sub.id);\n this.#subs.delete(group.sub.id);\n // The wire has no handlers, so `end` fires nothing.\n group.sub.end(reason);\n }\n\n #detachLive(group: LiveGroup, handle: Subscription): void {\n if (!group.attachments.delete(handle)) {\n return;\n }\n if (group.attachments.size > 0) {\n return;\n }\n this.#endLiveGroup(group, { sendClose: true, reason: \"closed by client\" });\n }\n\n #endLiveGroup(group: LiveGroup, opts: { sendClose: boolean; reason: string }): void {\n const { id } = group.sub;\n const remaining = [...group.attachments];\n group.attachments.clear();\n this.#forgetLiveGroup(group, opts.reason);\n if (opts.sendClose) {\n try {\n if (this.#isOpen()) {\n this.#send([\"CLOSE\", id]);\n }\n } catch {\n // ignore\n }\n }\n for (const att of remaining) {\n invokeSafely(() => {\n att.close(opts.reason);\n });\n }\n }\n\n #deliverLiveEvent(group: LiveGroup, event: Event): void {\n const { sub } = group;\n const attachments = [...group.attachments];\n for (const att of attachments) {\n att.notifyReceived(event.id);\n }\n if (sub.isAtWatermark(event.id)) {\n return;\n }\n\n const recipients: Subscription[] = [];\n for (const att of attachments) {\n if (att.closed) {\n continue;\n }\n if (!att.alreadyHas(event.id)) {\n recipients.push(att);\n }\n }\n if (recipients.length === 0) {\n return;\n }\n if (!this.#acceptEvent(event)) {\n return;\n }\n\n sub.noteVerified(event);\n for (const att of recipients) {\n att.deliver(event);\n }\n }\n\n #deliverLiveEose(group: LiveGroup): void {\n // snapshot: a oneose handler may close its handle and detach mid-pass\n const attachments = [...group.attachments];\n for (const att of attachments) {\n att.markEose();\n }\n }\n}\n","import { raceSignal, throwIfAborted } from \"../core/abort.ts\";\nimport { Emitter } from \"../core/emitter.ts\";\nimport { MessageError, WasmPoisonedError } from \"../core/error.ts\";\nimport type { Event, EventTemplate } from \"../core/event.ts\";\nimport { canonicalizeFilter, canonicalizeFilters } from \"../core/filter.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport {\n assertSubscriptionId,\n createSubscriptionId,\n encodeClientMessage,\n parseRelayMessage,\n} from \"../core/message.ts\";\nimport type { ClientMessage, CountResult, SubscriptionId } from \"../core/message.ts\";\nimport { normalizeURL } from \"../core/util.ts\";\nimport { verifyEvent } from \"../core/verifier.ts\";\nimport type { EventVerifier } from \"../core/verifier.ts\";\nimport { isAuthRequired } from \"../nips/nip42.ts\";\nimport type { NegentropyStorageVector } from \"../nips/nip77.ts\";\nimport { RelayAuth } from \"./auth.ts\";\nimport { CountTracker } from \"./count-tracker.ts\";\nimport {\n RelayClosedError,\n RelayConnectionError,\n RelayError,\n RelayPublishError,\n RelayTimeoutError,\n} from \"./error.ts\";\nimport { fetchFilters, streamFilters } from \"./fetch.ts\";\nimport type { RelayFetchResult } from \"./fetch.ts\";\nimport { dispatchNegMessage, failAllNegSessions, runTrackedNegSession } from \"./neg-session.ts\";\nimport type { NegSession } from \"./neg-session.ts\";\nimport { OkTracker } from \"./ok-tracker.ts\";\nimport type { PublishResult } from \"./ok-tracker.ts\";\nimport { DEFAULT_PING_INTERVAL_MS, DEFAULT_PING_TIMEOUT_MS, PingLoop } from \"./ping.ts\";\nimport { SubscriptionRegistry } from \"./subscription-registry.ts\";\nimport type { Closer, RelaySubscription, SubscribeOptions } from \"./subscription.ts\";\nimport { getWebSocketImplementation } from \"./websocket.ts\";\nimport type { WebSocketConstructor, WebSocketLike } from \"./websocket.ts\";\n\n/**\n * Relay lifecycle states:\n *\n * - `initialized` — constructed; `connect()` not yet attempted.\n * - `connecting` — a `connect()` attempt is in flight (socket opening / resubscribing).\n * - `connected` — socket open; REQ/EVENT/COUNT flow.\n * - `disconnected` — socket down: a reconnect is scheduled when `enableReconnect` has live\n * subscriptions to restore, or `disconnect()` severed manually (REQ state kept).\n * - `closed` — terminal until the next `connect()`: `close()` or a socket death without a scheduled\n * reconnect.\n */\nexport const RelayStatus = {\n Initialized: \"initialized\",\n Connecting: \"connecting\",\n Connected: \"connected\",\n Disconnected: \"disconnected\",\n Closed: \"closed\",\n} as const;\n/** Union of the {@link RelayStatus} values. */\nexport type RelayStatusName = (typeof RelayStatus)[keyof typeof RelayStatus];\n\n/** Per-relay options: socket override, verification, timeouts, reconnect, ping, NIP-42 auth. */\nexport type RelayOptions = {\n websocketImplementation?: WebSocketConstructor | undefined;\n verifyEvent?: EventVerifier | undefined;\n publishTimeoutMs?: number | undefined;\n connectTimeoutMs?: number | undefined;\n /**\n * When true, unexpected disconnects schedule reconnect with backoff and re-fire open\n * subscriptions. Default false.\n */\n enableReconnect?: boolean | undefined;\n /** Backoff delays in ms between reconnect attempts. */\n reconnectBackoffMs?: ReadonlyArray<number> | undefined;\n enablePing?: boolean | undefined;\n pingIntervalMs?: number | undefined;\n pingTimeoutMs?: number | undefined;\n /** When set, CLOSED/OK `auth-required:` triggers AUTH then retries the REQ/EVENT/COUNT. */\n authSigner?: ((template: EventTemplate) => Promise<Event>) | undefined;\n};\n\n/** Typed event payloads for {@link Relay.on}: lifecycle and protocol notifications. */\nexport type RelayEventMap = {\n /** A NIP-01 NOTICE message from the relay. */\n notice: string;\n /** Terminal close of the relay (a transient disconnect with reconnect does not fire it). */\n close: undefined;\n /** Fired when the relay sends a NIP-42 AUTH challenge. */\n auth: string;\n /** Fired after a successful reconnect (not the initial connect). */\n reconnect: undefined;\n /**\n * Fired when a delivered EVENT fails id/signature verification, just before the event is dropped.\n * A poisoned verifier dropping events does not fire it.\n */\n invalidevent: undefined;\n};\n\ntype SocketHandlers = {\n onOpen: () => void;\n onError: () => void;\n onClose: () => void;\n onMessage: (ev: unknown) => void;\n ws: WebSocketLike;\n};\n\nexport const DEFAULT_REQUEST_TIMEOUT_MS = 4400;\nexport const DEFAULT_CONNECT_TIMEOUT_MS = 5000;\n\nconst DEFAULT_BACKOFF = [1000, 2000, 5000, 10_000, 20_000, 30_000, 60_000];\n\nconst noop = (): void => {\n // placeholder until the Promise executor captures resolve/reject\n};\n\n/**\n * Single-relay NIP-01 client. Connection lifecycle, REQ/CLOSE, EVENT publish ACK, AUTH, COUNT,\n * NIP-77, optional reconnect.\n */\nexport class Relay {\n readonly url: string;\n #ws: WebSocketLike | undefined;\n #gen = 0;\n #status: RelayStatusName = RelayStatus.Initialized;\n #connecting: Promise<void> | undefined;\n #connectFinish: ((err?: unknown) => void) | undefined;\n #connectTimer: ReturnType<typeof setTimeout> | undefined;\n #socketHandlers: SocketHandlers | undefined;\n readonly #subscriptions: SubscriptionRegistry;\n readonly #ok = new OkTracker();\n readonly #countRequests = new CountTracker();\n readonly #authState: RelayAuth;\n readonly #ensureAuthed: () => Promise<boolean>;\n readonly #neg = new Map<SubscriptionId, NegSession>();\n readonly #WS: WebSocketConstructor;\n readonly #verify: EventVerifier;\n #verifyDead = false;\n readonly #publishTimeoutMs: number;\n readonly #connectTimeoutMs: number;\n readonly #enableReconnect: boolean;\n readonly #backoff: ReadonlyArray<number>;\n #serial = 0;\n readonly #authSigner: ((template: EventTemplate) => Promise<Event>) | undefined;\n /** `close()`/`disconnect()` was called; cleared by the next `connect()`. */\n #manualStop = false;\n #reconnectAttempts = 0;\n #reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n readonly #enablePing: boolean;\n readonly #ping: PingLoop;\n readonly #events = new Emitter<RelayEventMap>();\n\n constructor(url: string, opts: RelayOptions = {}) {\n this.url = normalizeURL(url);\n this.#WS = opts.websocketImplementation ?? getWebSocketImplementation();\n this.#verify = opts.verifyEvent ?? verifyEvent;\n this.#publishTimeoutMs = opts.publishTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.#connectTimeoutMs = opts.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;\n this.#enableReconnect = opts.enableReconnect ?? false;\n this.#backoff = opts.reconnectBackoffMs ?? DEFAULT_BACKOFF;\n this.#enablePing = opts.enablePing ?? false;\n this.#authSigner = opts.authSigner;\n this.#ping = new PingLoop({\n send: (message) => this.#send(message),\n nextSubId: (prefix) => this.nextSubId(prefix),\n getWs: () => this.#ws,\n closeWs: () => {\n const ws = this.#ws;\n if (ws && ws.readyState === this.#WS.OPEN) {\n try {\n ws.close();\n } catch {\n // ignore\n }\n }\n },\n pingIntervalMs: opts.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS,\n pingTimeoutMs: opts.pingTimeoutMs ?? DEFAULT_PING_TIMEOUT_MS,\n });\n this.#subscriptions = new SubscriptionRegistry({\n isOpen: () => this.#isOpen(),\n enableReconnect: this.#enableReconnect,\n send: (message) => this.#send(message),\n scheduleReconnect: () => this.#scheduleReconnect(),\n acceptEvent: (event) => this.#acceptEvent(event),\n });\n this.#authState = new RelayAuth({\n url: this.url,\n isOpen: () => this.#isOpen(),\n // oxlint-disable-next-line typescript/promise-function-async -- returns the tracker promise directly\n sendAuth: (event, timeoutMs) =>\n this.#ok.track(event.id, {\n timeoutMs,\n timeoutError: () => new RelayTimeoutError(\"auth timed out\", this.url),\n sendError: () => new RelayPublishError(\"auth send failed\", this.url),\n send: () => this.#send([\"AUTH\", event]),\n }),\n defaultTimeoutMs: this.#publishTimeoutMs,\n });\n this.#ensureAuthed = this.#authState.ensureAuthed.bind(this.#authState, this.#authSigner);\n }\n\n /** Latest NIP-42 challenge, if any. */\n get challenge(): string | undefined {\n return this.#authState.challenge;\n }\n\n get connected(): boolean {\n return this.#isOpen();\n }\n\n get status(): RelayStatusName {\n return this.#status;\n }\n\n #isOpen(): boolean {\n return this.#status === RelayStatus.Connected;\n }\n\n #setStatus(next: RelayStatusName): void {\n this.#status = next;\n }\n\n get generation(): number {\n return this.#gen;\n }\n\n get reconnectEnabled(): boolean {\n return this.#enableReconnect;\n }\n\n /** User subscriptions only; dummy ping REQs are not counted. */\n get subscriptionCount(): number {\n return this.#subscriptions.size;\n }\n\n /** One-shot requests still awaiting a relay reply (EVENT ACK, COUNT, NEG). */\n get inFlightCount(): number {\n return this.#ok.size + this.#countRequests.size + this.#neg.size;\n }\n\n /**\n * Listen for a relay event. Multiple listeners per type are allowed and fire in registration\n * order; a throwing listener is reported and does not break the dispatch. Returns an unsubscribe\n * function.\n */\n on<K extends keyof RelayEventMap>(\n type: K,\n listener: (payload: RelayEventMap[K]) => void,\n ): () => void {\n return this.#events.on(type, listener);\n }\n\n static async connect(\n url: string,\n opts?: RelayOptions & { signal?: AbortSignal },\n ): Promise<Relay> {\n const relay = new Relay(url, opts);\n await relay.connect({ signal: opts?.signal });\n return relay;\n }\n\n async connect(opts?: {\n signal?: AbortSignal | undefined;\n timeoutMs?: number | undefined;\n }): Promise<void> {\n if (this.#isOpen()) {\n return;\n }\n // A connect attempt is never owned by a caller's signal: joiners race\n // their own signal against the shared attempt; only close() cancels it.\n if (this.#connecting) {\n await raceSignal(this.#connecting, opts?.signal);\n return;\n }\n throwIfAborted(opts?.signal);\n\n const gen = ++this.#gen;\n this.#manualStop = false;\n this.#clearReconnectTimer();\n this.#setStatus(RelayStatus.Connecting);\n\n const timeoutMs = opts?.timeoutMs ?? this.#connectTimeoutMs;\n\n let resolveConnect: () => void = noop;\n let rejectConnect: (err: unknown) => void = noop;\n let settled = false;\n let ws: WebSocketLike | undefined;\n\n const connecting = new Promise<void>((resolve, reject) => {\n resolveConnect = resolve;\n rejectConnect = reject;\n });\n\n const release = (): void => {\n if (ws === undefined) {\n return;\n }\n this.#removeSocketListeners(handlers);\n if (this.#socketHandlers === handlers) {\n this.#socketHandlers = undefined;\n }\n try {\n if (ws.readyState !== this.#WS.CLOSED && ws.readyState !== this.#WS.CLOSING) {\n ws.close();\n }\n } catch {\n // ignore\n }\n if (this.#ws === ws) {\n this.#ws = undefined;\n }\n };\n\n // Stale-generation guard: events from a superseded socket release it before returning;\n // onMessage is the exception — it only ignores them.\n const guarded = (fn: () => void): (() => void) => {\n return () => {\n if (gen !== this.#gen) {\n release();\n return;\n }\n fn();\n };\n };\n\n const finish = (err?: unknown): void => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n if (this.#connectTimer === timer) {\n this.#connectTimer = undefined;\n }\n if (this.#connectFinish === finish) {\n this.#connectFinish = undefined;\n }\n if (this.#connecting === connecting) {\n this.#connecting = undefined;\n }\n if (err === undefined) {\n resolveConnect();\n } else {\n rejectConnect(err);\n }\n };\n\n this.#connecting = connecting;\n this.#connectFinish = finish;\n\n const timer = setTimeout(\n guarded(() => {\n release();\n finish(new RelayTimeoutError(\"connection timed out\", this.url));\n if (gen === this.#gen) {\n this.#handleSocketDeath(\"connection timed out\", { fromConnectAttempt: true, gen });\n }\n }),\n timeoutMs,\n );\n this.#connectTimer = timer;\n\n try {\n ws = new this.#WS(this.url);\n } catch (error) {\n finish(error);\n if (gen === this.#gen) {\n this.#handleSocketDeath(\"connection failed\", { fromConnectAttempt: true, gen });\n }\n await raceSignal(connecting, opts?.signal);\n return;\n }\n this.#ws = ws;\n\n const onOpen = guarded((): void => {\n this.#setStatus(RelayStatus.Connected);\n const wasReconnect = this.#reconnectAttempts > 0;\n this.#authState.resetConnection();\n if (!this.#subscriptions.replayAll()) {\n this.#setStatus(RelayStatus.Disconnected);\n release();\n finish(new RelayConnectionError(\"connection failed\", this.url));\n if (this.#enableReconnect && !this.#manualStop && this.#subscriptions.size > 0) {\n this.#scheduleReconnect();\n }\n return;\n }\n this.#reconnectAttempts = 0;\n if (this.#enablePing) {\n this.#ping.start();\n } else {\n this.#ping.stop();\n }\n if (wasReconnect) {\n this.#events.emit(\"reconnect\", undefined);\n }\n finish();\n });\n const onError = guarded((): void => {\n const fromConnectAttempt = !settled;\n release();\n finish(new RelayConnectionError(\"connection failed\", this.url));\n if (gen === this.#gen) {\n this.#handleSocketDeath(\"connection failed\", { fromConnectAttempt, gen });\n }\n });\n const onClose = guarded((): void => {\n const fromConnectAttempt = !settled;\n release();\n if (fromConnectAttempt) {\n finish(new RelayConnectionError(\"websocket closed\", this.url));\n }\n if (gen === this.#gen) {\n this.#handleSocketDeath(\"websocket closed\", { fromConnectAttempt, gen });\n }\n });\n const onMessage = (ev: unknown): void => {\n if (gen !== this.#gen) {\n return;\n }\n const data = typeof ev === \"object\" && ev !== null && \"data\" in ev ? ev.data : ev;\n this.#onMessage(String(data));\n };\n\n const handlers: SocketHandlers = { onOpen, onError, onClose, onMessage, ws };\n this.#socketHandlers = handlers;\n ws.addEventListener(\"open\", onOpen);\n ws.addEventListener(\"error\", onError);\n ws.addEventListener(\"close\", onClose);\n ws.addEventListener(\"message\", onMessage);\n\n await raceSignal(connecting, opts?.signal);\n }\n\n #removeSocketListeners(h: SocketHandlers): void {\n try {\n h.ws.removeEventListener(\"open\", h.onOpen);\n h.ws.removeEventListener(\"error\", h.onError);\n h.ws.removeEventListener(\"close\", h.onClose);\n h.ws.removeEventListener(\"message\", h.onMessage);\n } catch {\n // ignore\n }\n }\n\n #detachSocketHandlers(): void {\n const h = this.#socketHandlers;\n if (!h) {\n return;\n }\n this.#removeSocketListeners(h);\n this.#socketHandlers = undefined;\n }\n\n /** Graceful shutdown: disables reconnect and closes all subscriptions. */\n close(): void {\n this.#gen += 1;\n this.#setStatus(RelayStatus.Closed);\n this.#manualStop = true;\n this.#clearReconnectTimer();\n if (this.#connectTimer !== undefined) {\n clearTimeout(this.#connectTimer);\n this.#connectTimer = undefined;\n }\n this.#ping.stop();\n this.#connectFinish?.(new RelayClosedError(\"relay closed\", this.url));\n try {\n this.#subscriptions.closeAll(\"relay closed\");\n } finally {\n this.#failPending(new RelayClosedError(\"relay closed\", this.url));\n this.#detachSocketHandlers();\n this.#teardownSocket();\n this.#events.emit(\"close\", undefined);\n }\n }\n\n /**\n * Sever the socket while keeping every subscription: unlike {@link close}, open REQ state is\n * preserved and reconnect stays suppressed — the next {@link connect} re-subscribes all of them.\n * {@link Pool} uses this to suspend a relay without tearing down its live subscriptions.\n */\n disconnect(): void {\n this.#gen += 1;\n this.#setStatus(RelayStatus.Disconnected);\n this.#manualStop = true;\n this.#clearReconnectTimer();\n if (this.#connectTimer !== undefined) {\n clearTimeout(this.#connectTimer);\n this.#connectTimer = undefined;\n }\n this.#ping.stop();\n this.#connectFinish?.(new RelayClosedError(\"relay disconnected\", this.url));\n this.#failPending(new RelayClosedError(\"relay disconnected\", this.url));\n this.#detachSocketHandlers();\n this.#teardownSocket();\n }\n\n #teardownSocket(): void {\n try {\n this.#ws?.close();\n } catch {\n // ignore\n }\n this.#ws = undefined;\n }\n\n #clearReconnectTimer(): void {\n if (this.#reconnectTimer !== undefined) {\n clearTimeout(this.#reconnectTimer);\n this.#reconnectTimer = undefined;\n }\n }\n\n #failPending(err: Error): void {\n this.#ok.rejectAll(err);\n this.#countRequests.rejectAll(err);\n failAllNegSessions(this.#neg, err);\n }\n\n /** Unexpected socket death. Keep subscriptions if reconnecting. */\n #handleSocketDeath(reason: string, opts: { fromConnectAttempt?: boolean; gen: number }): void {\n if (opts.gen !== this.#gen) {\n return;\n }\n // Stale-gen guards retire every later event from this socket (error+close pairs collapse).\n this.#gen += 1;\n this.#ping.stop();\n this.#detachSocketHandlers();\n this.#ws = undefined;\n this.#failPending(new RelayClosedError(reason, this.url));\n\n const canReconnect = this.#enableReconnect && !this.#manualStop && this.#subscriptions.size > 0;\n\n if (canReconnect) {\n this.#setStatus(RelayStatus.Disconnected);\n this.#scheduleReconnect();\n return;\n }\n\n // Transitional: subscription close callbacks below observe `disconnected` first.\n this.#setStatus(RelayStatus.Disconnected);\n\n if (opts.fromConnectAttempt !== true || this.#subscriptions.size > 0) {\n this.#subscriptions.closeAll(reason);\n if (!this.#manualStop) {\n this.#events.emit(\"close\", undefined);\n }\n }\n\n this.#setStatus(RelayStatus.Closed);\n }\n\n #scheduleReconnect(): void {\n if (this.#reconnectTimer !== undefined) {\n return;\n }\n if (this.#isOpen()) {\n return;\n }\n const delay =\n this.#backoff[Math.min(this.#reconnectAttempts, this.#backoff.length - 1)] ?? 60_000;\n this.#reconnectAttempts += 1;\n this.#reconnectTimer = setTimeout(() => {\n this.#reconnectTimer = undefined;\n if (this.#manualStop || this.#isOpen()) {\n return;\n }\n void (async (): Promise<void> => {\n try {\n await this.connect();\n } catch {\n if (\n this.#enableReconnect &&\n !this.#manualStop &&\n this.#subscriptions.size > 0 &&\n !this.#isOpen()\n ) {\n this.#scheduleReconnect();\n }\n }\n })();\n }, delay);\n }\n\n #send(message: ClientMessage | string): void {\n if (!this.#ws || !this.#isOpen()) {\n throw new RelayClosedError(\"not connected\", this.url);\n }\n const raw = typeof message === \"string\" ? message : encodeClientMessage(message);\n this.#ws.send(raw);\n }\n\n #onMessage(raw: string): void {\n let msg;\n try {\n msg = parseRelayMessage(raw);\n } catch {\n return;\n }\n\n switch (msg[0]) {\n case \"EVENT\": {\n const [, subId, event] = msg;\n if (this.#ping.hasWaiter(subId)) {\n return;\n }\n this.#subscriptions.handleEvent(subId, event);\n break;\n }\n case \"EOSE\": {\n const [, subId] = msg;\n if (this.#ping.finishDummyPing(subId)) {\n return;\n }\n this.#subscriptions.handleEose(subId);\n break;\n }\n case \"CLOSED\": {\n const [, subId, reason] = msg;\n if (this.#ping.finishDummyPing(subId)) {\n return;\n }\n const countWaiter = this.#countRequests.get(subId);\n if (countWaiter) {\n if (isAuthRequired(reason) && this.#authSigner && !countWaiter.authRetried) {\n countWaiter.authRetried = true;\n void this.#countRequests.authRetry(countWaiter, {\n ensureAuthed: this.#ensureAuthed,\n isOpen: () => this.#isOpen(),\n failError: () => new RelayClosedError(reason || \"COUNT closed\", this.url),\n send: () => this.#send([\"COUNT\", subId, ...countWaiter.filters]),\n });\n return;\n }\n countWaiter.reject(new RelayClosedError(reason || \"COUNT closed\", this.url));\n return;\n }\n const sub = this.#subscriptions.get(subId);\n if (!sub) {\n return;\n }\n if (isAuthRequired(reason) && this.#authSigner && sub.beginAuthRetry()) {\n void this.#subscriptions.authRetry(sub, reason, this.#ensureAuthed);\n return;\n }\n this.#subscriptions.drop(sub, reason);\n break;\n }\n case \"OK\": {\n const [, eventId, ok, message] = msg;\n const waiter = this.#ok.handleOk(\n eventId,\n ok,\n message,\n (w) =>\n !ok &&\n isAuthRequired(message) &&\n w.event !== undefined &&\n !w.authRetried &&\n this.#authSigner !== undefined,\n );\n if (!waiter) {\n return;\n }\n waiter.authRetried = true;\n void this.#ok.authRetry(waiter, message, {\n ensureAuthed: this.#ensureAuthed,\n isOpen: () => this.#isOpen(),\n send: (event) => this.#send([\"EVENT\", event]),\n });\n return;\n }\n case \"COUNT\": {\n const [, countId, payload] = msg;\n const waiter = this.#countRequests.get(countId);\n if (!waiter) {\n return;\n }\n waiter.resolve(payload);\n break;\n }\n case \"NEG-MSG\":\n case \"NEG-ERR\":\n dispatchNegMessage(this.#neg, msg);\n break;\n case \"NOTICE\": {\n const [, notice] = msg;\n this.#events.emit(\"notice\", notice);\n break;\n }\n case \"AUTH\": {\n const [, authChallenge] = msg;\n this.#authState.handleChallenge(authChallenge);\n this.#events.emit(\"auth\", authChallenge);\n break;\n }\n }\n }\n\n /** Low-level REQ with callbacks. Survives reconnect when enableReconnect is on. */\n subscribe(filters: ReadonlyArray<Filter>, opts: SubscribeOptions = {}): RelaySubscription {\n if (filters.length === 0) {\n throw new MessageError(\"REQ requires at least one filter\");\n }\n if (!this.#isOpen() && !this.#enableReconnect) {\n throw new RelayClosedError(\"not connected\", this.url);\n }\n const canonical = canonicalizeFilters(filters);\n return this.#subscriptions.open(canonical, opts);\n }\n\n #acceptEvent(event: Event): boolean {\n if (this.#verifyDead) {\n return false;\n }\n try {\n const ok = this.#verify(event);\n if (!ok) {\n this.#events.emit(\"invalidevent\", undefined);\n }\n return ok;\n } catch (error) {\n if (error instanceof WasmPoisonedError) {\n this.#verifyDead = true;\n this.#events.emit(\"notice\", \"wasm-poisoned: instance aborted\");\n return false;\n }\n throw error;\n }\n }\n\n /** AsyncIterable of events for filters until the subscription is closed. */\n stream(\n filters: ReadonlyArray<Filter>,\n opts?: { signal?: AbortSignal | undefined; id?: string | undefined },\n ): AsyncIterable<Event> & Closer {\n return streamFilters((f, o) => this.subscribe(f, o), filters, opts);\n }\n\n /**\n * One-shot query: collect events until the REQ ends, then close. `end` reports how it ended —\n * relay `EOSE`, relay `CLOSED` (with its reason), or the local timeout; events received before a\n * CLOSED or the deadline are still returned. Abort rejects; a failed (re)connect rejects.\n */\n async fetch(\n filters: ReadonlyArray<Filter>,\n opts?: {\n timeoutMs?: number | undefined;\n signal?: AbortSignal | undefined;\n id?: string | undefined;\n },\n ): Promise<RelayFetchResult> {\n if (filters.length === 0) {\n throw new MessageError(\"REQ requires at least one filter\");\n }\n const canonical = canonicalizeFilters(filters);\n if (!this.#isOpen()) {\n await this.connect({ signal: opts?.signal });\n }\n return fetchFilters((f, o) => this.subscribe(f, o), canonical, {\n timeoutMs: opts?.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,\n signal: opts?.signal,\n id: opts?.id,\n });\n }\n\n /**\n * Publish an event and wait for OK. A concurrent publish of the same event id joins the in-flight\n * publish instead of racing it — the joiner's `timeoutMs` does not apply.\n */\n async publish(event: Event, opts?: { timeoutMs?: number | undefined }): Promise<PublishResult> {\n if (!this.#isOpen()) {\n throw new RelayClosedError(\"not connected\", this.url);\n }\n const existing = this.#ok.inFlight(event.id);\n if (existing !== undefined) {\n return existing;\n }\n const timeoutMs = opts?.timeoutMs ?? this.#publishTimeoutMs;\n return this.#ok.track(event.id, {\n timeoutMs,\n timeoutError: () => new RelayTimeoutError(\"publish timed out\", this.url),\n sendError: () => new RelayPublishError(\"publish failed\", this.url),\n event,\n send: () => this.#send([\"EVENT\", event]),\n });\n }\n\n /**\n * NIP-45 COUNT: ask the relay how many events match `filters`. Resolves with the COUNT payload\n * (count / optional approximate / optional hll).\n */\n async count(\n filters: ReadonlyArray<Filter>,\n opts?: {\n id?: string | undefined;\n timeoutMs?: number | undefined;\n signal?: AbortSignal | undefined;\n },\n ): Promise<CountResult> {\n if (!this.#isOpen()) {\n throw new RelayClosedError(\"not connected\", this.url);\n }\n if (filters.length === 0) {\n throw new RelayError(\"COUNT requires at least one filter\", this.url);\n }\n throwIfAborted(opts?.signal);\n const canonical = canonicalizeFilters(filters);\n\n const id = opts?.id === undefined ? this.nextSubId(\"count\") : createSubscriptionId(opts.id);\n const timeoutMs = opts?.timeoutMs ?? this.#publishTimeoutMs;\n\n return this.#countRequests.track(id, {\n timeoutMs,\n timeoutError: () => new RelayTimeoutError(\"count timed out\", this.url),\n sendError: () => new RelayPublishError(\"count failed\", this.url),\n filters: [...canonical],\n signal: opts?.signal,\n send: () => this.#send([\"COUNT\", id, ...canonical]),\n });\n }\n\n /** NIP-42 AUTH: sign the current challenge and wait for OK. */\n async auth(\n sign: (template: EventTemplate) => Promise<Event>,\n opts?: { timeoutMs?: number | undefined },\n ): Promise<PublishResult> {\n return this.#authState.auth(sign, opts);\n }\n\n /**\n * Clear a cached AUTH rejection for the current challenge so a later `auth()` signs again. A\n * successful auth and in-flight answers are kept. When a challenge is pending and unanswered, an\n * `auth` event is re-fired so the pool's automatic auth can run once more.\n */\n resetAuth(): void {\n const challenge = this.#authState.resetRejection();\n if (challenge !== undefined) {\n this.#events.emit(\"auth\", challenge);\n }\n }\n\n /**\n * NIP-77: run Negentropy reconciliation against this relay. Returns the local-only (`have`) and\n * remote-only (`need`) event ids. Does not upload or download events.\n *\n * `timeoutMs` is a single wall-clock deadline for the whole session (`NEG-OPEN` through the last\n * `NEG-MSG`), not a per-message budget. Default: {@link RelayOptions.publishTimeoutMs}.\n */\n async negReconcile(\n filter: Filter,\n storage: NegentropyStorageVector,\n opts?: {\n id?: string | undefined;\n timeoutMs?: number | undefined;\n signal?: AbortSignal | undefined;\n },\n ): Promise<{ have: string[]; need: string[] }> {\n if (!this.#isOpen()) {\n throw new RelayClosedError(\"not connected\", this.url);\n }\n throwIfAborted(opts?.signal);\n const id = opts?.id === undefined ? this.nextSubId(\"neg\") : assertSubscriptionId(opts.id);\n return runTrackedNegSession({\n sessions: this.#neg,\n id,\n storage,\n filter: canonicalizeFilter(filter),\n timeoutMs: opts?.timeoutMs ?? this.#publishTimeoutMs,\n signal: opts?.signal,\n send: (message) => this.#send(message),\n url: this.url,\n });\n }\n\n /** Generate a unique subscription id for this relay instance. */\n nextSubId(prefix = \"sub\"): string {\n this.#serial += 1;\n return `${prefix}:${this.#serial}`;\n }\n}\n\nexport type { CountResult } from \"../core/message.ts\";\nexport type { PublishResult } from \"./ok-tracker.ts\";\nexport type { RelayFetchEnd, RelayFetchResult } from \"./fetch.ts\";\nexport type { SubscribeOptions, SubscriptionHandlers } from \"./subscription.ts\";\n"],"mappings":"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"}