@qntx/nostr 0.12.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/dist/{builder-BrmLOVj_.d.mts → builder-C72nJITG.d.mts} +2 -2
  2. package/dist/{builder-Cf2N12CG.mjs → builder-CGgM_QHT.mjs} +5 -5
  3. package/dist/{builder-Cf2N12CG.mjs.map → builder-CGgM_QHT.mjs.map} +1 -1
  4. package/dist/{client-B3_vToDX.mjs → client-COkEihj_.mjs} +87 -79
  5. package/dist/client-COkEihj_.mjs.map +1 -0
  6. package/dist/client.d.mts +1 -1
  7. package/dist/client.mjs +1 -1
  8. package/dist/core.d.mts +7 -7
  9. package/dist/core.mjs +9 -9
  10. package/dist/{emitter-DkCY6NkU.mjs → emitter-u4kJqQDk.mjs} +2 -2
  11. package/dist/{emitter-DkCY6NkU.mjs.map → emitter-u4kJqQDk.mjs.map} +1 -1
  12. package/dist/{error-BquXDmIN.d.mts → error-BPqAU5RB.d.mts} +2 -6
  13. package/dist/{error-BD_0QFKT.d.mts → error-BjSUsx6M.d.mts} +2 -2
  14. package/dist/{error-D3VNRVhL.mjs → error-Dyb89ahb.mjs} +2 -2
  15. package/dist/{error-D3VNRVhL.mjs.map → error-Dyb89ahb.mjs.map} +1 -1
  16. package/dist/{error-DH4guike.mjs → error-QyECUDIp.mjs} +2 -2
  17. package/dist/{error-DH4guike.mjs.map → error-QyECUDIp.mjs.map} +1 -1
  18. package/dist/{event-B3ua2_oL.mjs → event-DvM1f03b.mjs} +7 -3
  19. package/dist/event-DvM1f03b.mjs.map +1 -0
  20. package/dist/{filter-B16P33dz.mjs → filter-3jOh4uLN.mjs} +50 -20
  21. package/dist/filter-3jOh4uLN.mjs.map +1 -0
  22. package/dist/{filter-PG7r79rt.d.mts → filter-oYG2zjJ3.d.mts} +6 -2
  23. package/dist/{gossip-u0iBlZ1U.d.mts → gossip-BvCItMVZ.d.mts} +3 -3
  24. package/dist/{gossip-K_JIexQs.mjs → gossip-CRyhK6E6.mjs} +4 -4
  25. package/dist/{gossip-K_JIexQs.mjs.map → gossip-CRyhK6E6.mjs.map} +1 -1
  26. package/dist/gossip.d.mts +1 -1
  27. package/dist/gossip.mjs +1 -1
  28. package/dist/http-CuazNRIP.mjs.map +1 -1
  29. package/dist/{index-DD0Ywpay.d.mts → index-B2JlP2Jf.d.mts} +9 -9
  30. package/dist/{index-yq9xkONo.d.mts → index-DCobP2Jj.d.mts} +10 -10
  31. package/dist/{index-Hp26bgYG.d.mts → index-Dpj_MUxb.d.mts} +8 -5
  32. package/dist/{index-rKcmI6ex.d.mts → index-DzHmhHqD.d.mts} +3 -3
  33. package/dist/{index-SwMknboJ.d.mts → index-YID-zPDF.d.mts} +7 -7
  34. package/dist/{index-DxQO1eWA.d.mts → index-muydfuYD.d.mts} +21 -21
  35. package/dist/index.d.mts +19 -20
  36. package/dist/index.mjs +20 -20
  37. package/dist/{key-B218_SoL.mjs → key-D_oEutQ6.mjs} +5 -7
  38. package/dist/{key-B218_SoL.mjs.map → key-D_oEutQ6.mjs.map} +1 -1
  39. package/dist/{key-CwMVo14x.d.mts → key-DojW2QZl.d.mts} +3 -5
  40. package/dist/{kind-D8fENiiL.mjs → kind-I2Tvbe82.mjs} +6 -5
  41. package/dist/kind-I2Tvbe82.mjs.map +1 -0
  42. package/dist/{loaders-Xdbx-K61.mjs → loaders-BRh2riV9.mjs} +17 -16
  43. package/dist/loaders-BRh2riV9.mjs.map +1 -0
  44. package/dist/loaders.d.mts +2 -2
  45. package/dist/loaders.mjs +1 -1
  46. package/dist/{memory-VyPnOBU3.mjs → memory-D_GdEQYG.mjs} +2 -2
  47. package/dist/memory-D_GdEQYG.mjs.map +1 -0
  48. package/dist/memory-index-3GSrPIWj.mjs +671 -0
  49. package/dist/memory-index-3GSrPIWj.mjs.map +1 -0
  50. package/dist/{message-pYfVKxPn.mjs → message-BMfreCQc.mjs} +6 -6
  51. package/dist/message-BMfreCQc.mjs.map +1 -0
  52. package/dist/{nip17-BM4hJYBq.d.mts → nip17-BZvFQLcs.d.mts} +4 -4
  53. package/dist/{nip19-DpPN17bY.d.mts → nip19-D4RKn6GC.d.mts} +2 -2
  54. package/dist/{nip46-BHaxC9TZ.d.mts → nip46-i8L8_nIm.d.mts} +2 -2
  55. package/dist/{nip51-D_bu8Hph.d.mts → nip51-C_kTYcud.d.mts} +2 -2
  56. package/dist/{nip59-CGWfoItR.d.mts → nip59-DfJ6Moeu.d.mts} +2 -2
  57. package/dist/{nip65-DaWMg943.d.mts → nip65-CPNE0SV0.d.mts} +2 -2
  58. package/dist/{nip77-wHA_nEV5.d.mts → nip77-DhpoL7d8.d.mts} +2 -2
  59. package/dist/nips/blossom.d.mts +2 -2
  60. package/dist/nips/blossom.mjs +3 -3
  61. package/dist/nips/blossom.mjs.map +1 -1
  62. package/dist/nips/nip04.d.mts +9 -12
  63. package/dist/nips/nip04.mjs +38 -45
  64. package/dist/nips/nip04.mjs.map +1 -1
  65. package/dist/nips/nip05.d.mts +6 -3
  66. package/dist/nips/nip05.mjs +6 -3
  67. package/dist/nips/nip05.mjs.map +1 -1
  68. package/dist/nips/nip10.d.mts +2 -2
  69. package/dist/nips/nip10.mjs +4 -4
  70. package/dist/nips/nip11.d.mts +1 -1
  71. package/dist/nips/nip11.mjs +1 -1
  72. package/dist/nips/nip13.d.mts +1 -1
  73. package/dist/nips/nip13.mjs +1 -1
  74. package/dist/nips/nip13.mjs.map +1 -1
  75. package/dist/nips/nip17.d.mts +1 -1
  76. package/dist/nips/nip17.mjs +4 -4
  77. package/dist/nips/nip17.mjs.map +1 -1
  78. package/dist/nips/nip19.d.mts +1 -1
  79. package/dist/nips/nip19.mjs +2 -2
  80. package/dist/nips/nip19.mjs.map +1 -1
  81. package/dist/nips/nip21.d.mts +6 -3
  82. package/dist/nips/nip21.mjs +8 -5
  83. package/dist/nips/nip21.mjs.map +1 -1
  84. package/dist/nips/nip27.d.mts +1 -1
  85. package/dist/nips/nip27.mjs +15 -9
  86. package/dist/nips/nip27.mjs.map +1 -1
  87. package/dist/nips/nip42.mjs +2 -2
  88. package/dist/nips/nip44.mjs +1 -1
  89. package/dist/nips/nip46.d.mts +1 -1
  90. package/dist/nips/nip46.mjs +9 -7
  91. package/dist/nips/nip46.mjs.map +1 -1
  92. package/dist/nips/nip49.d.mts +1 -1
  93. package/dist/nips/nip49.mjs +1 -1
  94. package/dist/nips/nip51.d.mts +1 -1
  95. package/dist/nips/nip51.mjs +5 -5
  96. package/dist/nips/nip51.mjs.map +1 -1
  97. package/dist/nips/nip57.mjs +6 -5
  98. package/dist/nips/nip57.mjs.map +1 -1
  99. package/dist/nips/nip59.d.mts +1 -1
  100. package/dist/nips/nip59.mjs +5 -5
  101. package/dist/nips/nip59.mjs.map +1 -1
  102. package/dist/nips/nip65.d.mts +1 -1
  103. package/dist/nips/nip65.mjs +3 -3
  104. package/dist/nips/nip77.d.mts +1 -1
  105. package/dist/nips/nip77.mjs +1 -1
  106. package/dist/nips/nip77.mjs.map +1 -1
  107. package/dist/nips/nip98.d.mts +1 -1
  108. package/dist/nips/nip98.mjs +9 -6
  109. package/dist/nips/nip98.mjs.map +1 -1
  110. package/dist/{pool-DUuYJ0x4.d.mts → pool-MehgIIzm.d.mts} +20 -16
  111. package/dist/{pool-THuuOW4S.mjs → pool-lMT_mvzn.mjs} +25 -25
  112. package/dist/pool-lMT_mvzn.mjs.map +1 -0
  113. package/dist/{put-DRmV1wHD.mjs → put-BEM5j8xK.mjs} +5 -5
  114. package/dist/{put-DRmV1wHD.mjs.map → put-BEM5j8xK.mjs.map} +1 -1
  115. package/dist/{reactive-jyKrA9ej.d.mts → reactive-B7lPFgTS.d.mts} +3 -3
  116. package/dist/{reactive--pDpLt2H.mjs → reactive-DFHY-f00.mjs} +24 -9
  117. package/dist/reactive-DFHY-f00.mjs.map +1 -0
  118. package/dist/{relay-BGPqiGKF.mjs → relay-DIR77xQ2.mjs} +102 -89
  119. package/dist/relay-DIR77xQ2.mjs.map +1 -0
  120. package/dist/relay.d.mts +5 -5
  121. package/dist/relay.mjs +3 -3
  122. package/dist/{report-CHld4uWj.mjs → report-D2BM5dgn.mjs} +2 -2
  123. package/dist/{report-CHld4uWj.mjs.map → report-D2BM5dgn.mjs.map} +1 -1
  124. package/dist/{signer-CNTIeYH4.mjs → signer-DJcmC4nA.mjs} +17 -17
  125. package/dist/signer-DJcmC4nA.mjs.map +1 -0
  126. package/dist/signer.d.mts +1 -1
  127. package/dist/signer.mjs +2 -2
  128. package/dist/{storage-B5Wdt_Mn.mjs → storage-n2BE1bdY.mjs} +108 -179
  129. package/dist/storage-n2BE1bdY.mjs.map +1 -0
  130. package/dist/storage.d.mts +3 -3
  131. package/dist/storage.mjs +4 -4
  132. package/dist/store.d.mts +1 -1
  133. package/dist/store.mjs +1 -1
  134. package/dist/testing.d.mts +4 -4
  135. package/dist/testing.mjs +35 -10
  136. package/dist/testing.mjs.map +1 -1
  137. package/dist/{types-CqhnT-Mx.d.mts → types-DiVVyf1e.d.mts} +2 -2
  138. package/dist/{util-DDpPkiED.mjs → util-DfsyJ9nz.mjs} +30 -10
  139. package/dist/util-DfsyJ9nz.mjs.map +1 -0
  140. package/dist/{verifier-CjH1vnP2.mjs → verifier-DEHUy4AD.mjs} +3 -3
  141. package/dist/{verifier-CjH1vnP2.mjs.map → verifier-DEHUy4AD.mjs.map} +1 -1
  142. package/dist/{verifier-Cl24c9ph.d.mts → verifier-DhAbqVJF.d.mts} +2 -2
  143. package/dist/{websocket-CmeqvAkp.d.mts → websocket-DaCvJkF9.d.mts} +2 -5
  144. package/package.json +4 -3
  145. package/dist/client-B3_vToDX.mjs.map +0 -1
  146. package/dist/event-B3ua2_oL.mjs.map +0 -1
  147. package/dist/filter-B16P33dz.mjs.map +0 -1
  148. package/dist/kind-D8fENiiL.mjs.map +0 -1
  149. package/dist/loaders-Xdbx-K61.mjs.map +0 -1
  150. package/dist/memory-VyPnOBU3.mjs.map +0 -1
  151. package/dist/memory-index-Bhwe0ZdG.mjs +0 -374
  152. package/dist/memory-index-Bhwe0ZdG.mjs.map +0 -1
  153. package/dist/message-pYfVKxPn.mjs.map +0 -1
  154. package/dist/pool-THuuOW4S.mjs.map +0 -1
  155. package/dist/reactive--pDpLt2H.mjs.map +0 -1
  156. package/dist/relay-BGPqiGKF.mjs.map +0 -1
  157. package/dist/signer-CNTIeYH4.mjs.map +0 -1
  158. package/dist/storage-B5Wdt_Mn.mjs.map +0 -1
  159. package/dist/util-DDpPkiED.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"emitter-DkCY6NkU.mjs","names":[],"sources":["../src/core/emitter.ts"],"sourcesContent":["import { invokeSafely } from \"./report.ts\";\n\n/**\n * Typed multi-listener dispatch for long-lived objects: `M` maps each event name to its payload\n * type. Listeners fire synchronously in registration order off a snapshot, so unsubscribing or\n * registering during dispatch neither skips nor double-fires the in-flight pass; a throwing\n * listener is reported and does not starve the rest.\n */\nexport class Emitter<M extends Record<string, unknown>> {\n readonly #listeners: { [K in keyof M]?: Set<(payload: M[K]) => void> } = {};\n\n /** Register `listener` for `type`; returns an unsubscribe function. */\n on<K extends keyof M>(type: K, listener: (payload: M[K]) => void): () => void {\n let set = this.#listeners[type];\n if (set === undefined) {\n set = new Set<(payload: M[K]) => void>();\n this.#listeners[type] = set;\n }\n set.add(listener);\n return () => {\n set.delete(listener);\n };\n }\n\n emit<K extends keyof M>(type: K, payload: M[K]): void {\n const set = this.#listeners[type];\n if (set === undefined) {\n return;\n }\n // snapshot: listeners may unsubscribe or register during dispatch\n for (const listener of new Set(set)) {\n invokeSafely(() => listener(payload));\n }\n }\n}\n"],"mappings":";;;;;;;;AAQA,IAAa,UAAb,MAAwD;CACtD,aAAyE,CAAC;;CAG1E,GAAsB,MAAS,UAA+C;EAC5E,IAAI,MAAM,KAAK,WAAW;EAC1B,IAAI,QAAQ,KAAA,GAAW;GACrB,sBAAM,IAAI,IAA6B;GACvC,KAAK,WAAW,QAAQ;EAC1B;EACA,IAAI,IAAI,QAAQ;EAChB,aAAa;GACX,IAAI,OAAO,QAAQ;EACrB;CACF;CAEA,KAAwB,MAAS,SAAqB;EACpD,MAAM,MAAM,KAAK,WAAW;EAC5B,IAAI,QAAQ,KAAA,GACV;EAGF,KAAK,MAAM,YAAY,IAAI,IAAI,GAAG,GAChC,mBAAmB,SAAS,OAAO,CAAC;CAExC;AACF"}
1
+ {"version":3,"file":"emitter-u4kJqQDk.mjs","names":[],"sources":["../src/core/emitter.ts"],"sourcesContent":["import { invokeSafely } from \"./report.ts\";\n\n/**\n * Typed multi-listener dispatch for long-lived objects: `M` maps each event name to its payload\n * type. Listeners fire synchronously in registration order off a snapshot, so unsubscribing or\n * registering during dispatch neither skips nor double-fires the in-flight pass; a throwing\n * listener is reported and does not starve the rest.\n */\nexport class Emitter<M extends Record<string, unknown>> {\n readonly #listeners: { [K in keyof M]?: Set<(payload: M[K]) => void> } = {};\n\n /** Register `listener` for `type`; returns an unsubscribe function. */\n on<K extends keyof M>(type: K, listener: (payload: M[K]) => void): () => void {\n let set = this.#listeners[type];\n if (set === undefined) {\n set = new Set<(payload: M[K]) => void>();\n this.#listeners[type] = set;\n }\n set.add(listener);\n return () => {\n set.delete(listener);\n };\n }\n\n emit<K extends keyof M>(type: K, payload: M[K]): void {\n const set = this.#listeners[type];\n if (set === undefined) {\n return;\n }\n // snapshot: listeners may unsubscribe or register during dispatch\n for (const listener of new Set(set)) {\n invokeSafely(() => listener(payload));\n }\n }\n}\n"],"mappings":";;;;;;;;AAQA,IAAa,UAAb,MAAwD;CACtD,aAAyE,CAAC;;CAG1E,GAAsB,MAAS,UAA+C;EAC5E,IAAI,MAAM,KAAK,WAAW;EAC1B,IAAI,QAAQ,KAAA,GAAW;GACrB,sBAAM,IAAI,IAA6B;GACvC,KAAK,WAAW,QAAQ;EAC1B;EACA,IAAI,IAAI,QAAQ;EAChB,aAAa;GACX,IAAI,OAAO,QAAQ;EACrB;CACF;CAEA,KAAwB,MAAS,SAAqB;EACpD,MAAM,MAAM,KAAK,WAAW;EAC5B,IAAI,QAAQ,KAAA,GACV;EAGF,KAAK,MAAM,YAAY,IAAI,IAAI,GAAG,GAChC,mBAAmB,SAAS,OAAO,CAAC;CAExC;AACF"}
@@ -23,10 +23,6 @@ declare class CryptoError extends NostrError {
23
23
  declare class MessageError extends NostrError {
24
24
  name: string;
25
25
  }
26
- /** Wasm instance aborted; later calls on that instance fail. */
27
- declare class WasmPoisonedError extends NostrError {
28
- name: string;
29
- }
30
26
  //#endregion
31
- export { NostrError as a, MessageError as i, EventValidationError as n, UrlError as o, HexError as r, WasmPoisonedError as s, CryptoError as t };
32
- //# sourceMappingURL=error-BquXDmIN.d.mts.map
27
+ export { NostrError as a, MessageError as i, EventValidationError as n, UrlError as o, HexError as r, CryptoError as t };
28
+ //# sourceMappingURL=error-BPqAU5RB.d.mts.map
@@ -1,4 +1,4 @@
1
- import { a as NostrError } from "./error-BquXDmIN.mjs";
1
+ import { a as NostrError } from "./error-BPqAU5RB.mjs";
2
2
  //#region src/storage/error.d.ts
3
3
  /** Event-store I/O failure. Message must not include event content. */
4
4
  declare class StorageError extends NostrError {
@@ -6,4 +6,4 @@ declare class StorageError extends NostrError {
6
6
  }
7
7
  //#endregion
8
8
  export { StorageError as t };
9
- //# sourceMappingURL=error-BD_0QFKT.d.mts.map
9
+ //# sourceMappingURL=error-BjSUsx6M.d.mts.map
@@ -1,4 +1,4 @@
1
- import { y as NostrError } from "./util-DDpPkiED.mjs";
1
+ import { x as NostrError } from "./util-DfsyJ9nz.mjs";
2
2
  //#region src/signer/error.ts
3
3
  /**
4
4
  * Thrown by a lazy NIP-42 AUTH sign function when no signer is configured at challenge time.
@@ -15,4 +15,4 @@ var SignerDisposedError = class extends NostrError {
15
15
  //#endregion
16
16
  export { SignerDisposedError as n, NoSignerError as t };
17
17
 
18
- //# sourceMappingURL=error-D3VNRVhL.mjs.map
18
+ //# sourceMappingURL=error-Dyb89ahb.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-D3VNRVhL.mjs","names":[],"sources":["../src/signer/error.ts"],"sourcesContent":["import { NostrError } from \"../core/error.ts\";\n\n/**\n * Thrown by a lazy NIP-42 AUTH sign function when no signer is configured at challenge time.\n * {@link import(\"../relay/relay.ts\").Relay} catches it and ignores the challenge: no AUTH frame is\n * sent and the connection stays open.\n */\nexport class NoSignerError extends NostrError {\n override name = \"NoSignerError\";\n}\n\n/** Thrown when a method is called on a signer after {@link NostrSigner dispose}. */\nexport class SignerDisposedError extends NostrError {\n override name = \"SignerDisposedError\";\n}\n"],"mappings":";;;;;;;AAOA,IAAa,gBAAb,cAAmC,WAAW;CAC5C,OAAgB;AAClB;;AAGA,IAAa,sBAAb,cAAyC,WAAW;CAClD,OAAgB;AAClB"}
1
+ {"version":3,"file":"error-Dyb89ahb.mjs","names":[],"sources":["../src/signer/error.ts"],"sourcesContent":["import { NostrError } from \"../core/error.ts\";\n\n/**\n * Thrown by a lazy NIP-42 AUTH sign function when no signer is configured at challenge time.\n * {@link import(\"../relay/relay.ts\").Relay} catches it and ignores the challenge: no AUTH frame is\n * sent and the connection stays open.\n */\nexport class NoSignerError extends NostrError {\n override name = \"NoSignerError\";\n}\n\n/** Thrown when a method is called on a signer after {@link NostrSigner dispose}. */\nexport class SignerDisposedError extends NostrError {\n override name = \"SignerDisposedError\";\n}\n"],"mappings":";;;;;;;AAOA,IAAa,gBAAb,cAAmC,WAAW;CAC5C,OAAgB;AAClB;;AAGA,IAAa,sBAAb,cAAyC,WAAW;CAClD,OAAgB;AAClB"}
@@ -1,4 +1,4 @@
1
- import { y as NostrError } from "./util-DDpPkiED.mjs";
1
+ import { x as NostrError } from "./util-DfsyJ9nz.mjs";
2
2
  //#region src/storage/error.ts
3
3
  /** Event-store I/O failure. Message must not include event content. */
4
4
  var StorageError = class extends NostrError {
@@ -12,4 +12,4 @@ function toStorageError(err) {
12
12
  //#endregion
13
13
  export { toStorageError as n, StorageError as t };
14
14
 
15
- //# sourceMappingURL=error-DH4guike.mjs.map
15
+ //# sourceMappingURL=error-QyECUDIp.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-DH4guike.mjs","names":[],"sources":["../src/storage/error.ts"],"sourcesContent":["import { NostrError } from \"../core/error.ts\";\n\n/** Event-store I/O failure. Message must not include event content. */\nexport class StorageError extends NostrError {\n override name = \"StorageError\";\n}\n\nexport function toStorageError(err: unknown): StorageError {\n if (err instanceof StorageError) {\n return err;\n }\n if (err instanceof Error) {\n return new StorageError(err.message, { cause: err });\n }\n return new StorageError(String(err));\n}\n"],"mappings":";;;AAGA,IAAa,eAAb,cAAkC,WAAW;CAC3C,OAAgB;AAClB;AAEA,SAAgB,eAAe,KAA4B;CACzD,IAAI,eAAe,cACjB,OAAO;CAET,IAAI,eAAe,OACjB,OAAO,IAAI,aAAa,IAAI,SAAS,EAAE,OAAO,IAAI,CAAC;CAErD,OAAO,IAAI,aAAa,OAAO,GAAG,CAAC;AACrC"}
1
+ {"version":3,"file":"error-QyECUDIp.mjs","names":[],"sources":["../src/storage/error.ts"],"sourcesContent":["import { NostrError } from \"../core/error.ts\";\n\n/** Event-store I/O failure. Message must not include event content. */\nexport class StorageError extends NostrError {\n override name = \"StorageError\";\n}\n\nexport function toStorageError(err: unknown): StorageError {\n if (err instanceof StorageError) {\n return err;\n }\n if (err instanceof Error) {\n return new StorageError(err.message, { cause: err });\n }\n return new StorageError(String(err));\n}\n"],"mappings":";;;AAGA,IAAa,eAAb,cAAkC,WAAW;CAC3C,OAAgB;AAClB;AAEA,SAAgB,eAAe,KAA4B;CACzD,IAAI,eAAe,cACjB,OAAO;CAET,IAAI,eAAe,OACjB,OAAO,IAAI,aAAa,IAAI,SAAS,EAAE,OAAO,IAAI,CAAC;CAErD,OAAO,IAAI,aAAa,OAAO,GAAG,CAAC;AACrC"}
@@ -1,5 +1,5 @@
1
- import { a as hasLoneSurrogate, c as isHex64, g as EventValidationError, i as bytesToHex, l as isRecord, m as utf8Encoder, s as isHex32 } from "./util-DDpPkiED.mjs";
2
- import { o as isReplaceableKind, r as isAddressableKind } from "./kind-D8fENiiL.mjs";
1
+ import { c as isHex32, g as utf8Encoder, i as bytesToHex, l as isHex64, o as hasLoneSurrogate, u as isRecord, v as EventValidationError } from "./util-DfsyJ9nz.mjs";
2
+ import { o as isReplaceableKind, r as isAddressableKind } from "./kind-I2Tvbe82.mjs";
3
3
  import { sha256 } from "@noble/hashes/sha2.js";
4
4
  //#region src/core/tag.ts
5
5
  /** Type guard: a non-empty array of strings. */
@@ -105,6 +105,8 @@ function eventAddress(event) {
105
105
  const verifiedEvents = /* @__PURE__ */ new WeakSet();
106
106
  /** Cache of events known to fail verification. */
107
107
  const failedEvents = /* @__PURE__ */ new WeakSet();
108
+ /** Cache of object identities that already passed {@link validateSignedEvent}. */
109
+ const validatedEvents = /* @__PURE__ */ new WeakSet();
108
110
  function markVerified(event) {
109
111
  verifiedEvents.add(event);
110
112
  failedEvents.delete(event);
@@ -135,10 +137,12 @@ function validateEvent(event) {
135
137
  }
136
138
  /** Structural validation of a full event including id and sig hex shape. */
137
139
  function validateSignedEvent(event) {
140
+ if (isRecord(event) && validatedEvents.has(event)) return true;
138
141
  if (!validateEvent(event)) return false;
139
142
  const e = event;
140
143
  if (typeof e["id"] !== "string" || !isHex32(e["id"])) return false;
141
144
  if (typeof e["sig"] !== "string" || !isHex64(e["sig"])) return false;
145
+ validatedEvents.add(e);
142
146
  return true;
143
147
  }
144
148
  /**
@@ -222,4 +226,4 @@ function signedMatchesUnsigned(signed, unsigned) {
222
226
  //#endregion
223
227
  export { tagName as C, parseEventAddress as S, eventAddress as _, isReplaceableWinner as a, getDTag as b, markVerified as c, signedMatchesUnsigned as d, sortEvents as f, Tag as g, validateSignedEvent as h, isMarkedVerified as i, serializeEvent as l, validateEvent as m, getEventHash as n, itemCompare as o, sortedEvents as p, isMarkedFailed as r, markUnverified as s, compareEventsDesc as t, serializeValidatedEvent as u, firstTagValue as v, tagValue as w, isTag as x, formatEventAddress as y };
224
228
 
225
- //# sourceMappingURL=event-B3ua2_oL.mjs.map
229
+ //# sourceMappingURL=event-DvM1f03b.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-DvM1f03b.mjs","names":[],"sources":["../src/core/tag.ts","../src/core/event.ts"],"sourcesContent":["import { isAddressableKind, isReplaceableKind } from \"./kind.ts\";\nimport { isHex32 } from \"./util.ts\";\n\n/** A NIP-01 tag: first element is the name, rest are values. */\nexport type Tag = ReadonlyArray<string>;\n\n/** Mutable tag builder input. */\nexport type TagInput = string[];\n\n/** Type guard: a non-empty array of strings. */\nexport function isTag(value: unknown): value is Tag {\n if (!Array.isArray(value) || value.length === 0) {\n return false;\n }\n for (const item of value) {\n if (typeof item !== \"string\") {\n return false;\n }\n }\n return true;\n}\n\n/** First element of a tag (its name). */\nexport function tagName(tag: Tag): string {\n return tag[0] ?? \"\";\n}\n\n/** Second element of a tag (its primary value), or undefined for a bare name tag. */\nexport function tagValue(tag: Tag): string | undefined {\n return tag[1];\n}\n\n/**\n * Push positional tag values: \"\" for absent slots before a present one, trailing absent slots\n * omitted.\n */\nfunction pushPositions(t: string[], positions: Array<string | undefined>): void {\n let end = positions.length;\n while (end > 0 && positions[end - 1] === undefined) {\n end -= 1;\n }\n for (let i = 0; i < end; i += 1) {\n t.push(positions[i] ?? \"\");\n }\n}\n\n/** Construct common tags. */\nexport const Tag = {\n e(id: string, relay?: string, marker?: string, pubkey?: string): Tag {\n const t: string[] = [\"e\", id.toLowerCase()];\n pushPositions(t, [relay, marker, pubkey?.toLowerCase()]);\n return t;\n },\n p(pubkey: string, relay?: string, petname?: string): Tag {\n const t: string[] = [\"p\", pubkey.toLowerCase()];\n pushPositions(t, [relay, petname]);\n return t;\n },\n a(coordinate: string, relay?: string): Tag {\n const t: string[] = [\"a\", coordinate];\n if (relay !== undefined) {\n t.push(relay);\n }\n return t;\n },\n d(identifier: string): Tag {\n return [\"d\", identifier];\n },\n t(hashtag: string): Tag {\n return [\"t\", hashtag];\n },\n r(url: string, marker?: string): Tag {\n const t: string[] = [\"r\", url];\n if (marker !== undefined) {\n t.push(marker);\n }\n return t;\n },\n k(kind: number | string): Tag {\n return [\"k\", String(kind)];\n },\n} as const;\n\n/** First tag value with the given name, or undefined when no such tag has a value. */\nexport function firstTagValue(tags: ReadonlyArray<Tag>, name: string): string | undefined {\n for (const tag of tags) {\n if (tag[0] === name && tag[1] !== undefined) {\n return tag[1];\n }\n }\n return undefined;\n}\n\n/** First `d` tag value on an event, if any. */\nexport function getDTag(tags: ReadonlyArray<Tag>): string | undefined {\n return firstTagValue(tags, \"d\");\n}\n\n/** NIP-01 `a` tag: `kind:pubkey:identifier` (identifier may be empty and may contain extra colons). */\nexport type EventAddress = {\n kind: number;\n pubkey: string;\n identifier: string;\n};\n\n/** Parse a NIP-01 addressable/replaceable coordinate. */\nexport function parseEventAddress(value: string): EventAddress | undefined {\n const first = value.indexOf(\":\");\n if (first <= 0) {\n return undefined;\n }\n const second = value.indexOf(\":\", first + 1);\n if (second === -1) {\n return undefined;\n }\n const kindText = value.slice(0, first);\n // Strict decimal: Number() also accepts \"1e4\", \"0x10\", \" 1\", \"+1\" — none are NIP-19 kinds.\n if (!/^\\d{1,5}$/.test(kindText)) {\n return undefined;\n }\n const kind = Number(kindText);\n if (kind > 65535) {\n return undefined;\n }\n const pubkey = value.slice(first + 1, second).toLowerCase();\n if (!isHex32(pubkey)) {\n return undefined;\n }\n return { kind, pubkey, identifier: value.slice(second + 1) };\n}\n\n/** Format an address as a `kind:pubkey:dTag` coordinate string. */\nexport function formatEventAddress(kind: number, pubkey: string, identifier = \"\"): string {\n return `${kind}:${pubkey.toLowerCase()}:${identifier}`;\n}\n\n/**\n * Coordinate for replaceable (`kind:pubkey:`) and addressable (`kind:pubkey:d`) events. Undefined\n * for regular/ephemeral kinds.\n */\nexport function eventAddress(event: {\n kind: number;\n pubkey: string;\n tags: ReadonlyArray<Tag>;\n}): string | undefined {\n if (isAddressableKind(event.kind)) {\n return formatEventAddress(event.kind, event.pubkey, getDTag(event.tags) ?? \"\");\n }\n if (isReplaceableKind(event.kind)) {\n return formatEventAddress(event.kind, event.pubkey, \"\");\n }\n return undefined;\n}\n","import { sha256 } from \"@noble/hashes/sha2.js\";\n\nimport { EventValidationError } from \"./error.ts\";\nimport type { Tag } from \"./tag.ts\";\nimport { isTag } from \"./tag.ts\";\nimport { bytesToHex, hasLoneSurrogate, isHex32, isHex64, isRecord, utf8Encoder } from \"./util.ts\";\n\n/** Wire event template before pubkey/id/sig. */\nexport type EventTemplate = {\n readonly kind: number;\n readonly tags: ReadonlyArray<Tag>;\n readonly content: string;\n readonly created_at: number;\n};\n\n/** Event with pubkey, ready to hash and sign. */\nexport type UnsignedEvent = EventTemplate & {\n readonly pubkey: string;\n};\n\n/**\n * Fully formed NIP-01 event (JSON-friendly plain object). Fields are readonly at the type level;\n * wire interop remains plain JSON.\n */\nexport type Event = UnsignedEvent & {\n readonly id: string;\n readonly sig: string;\n};\n\n/** Cache of events whose signatures have been verified successfully. */\nconst verifiedEvents = new WeakSet<Event>();\n\n/** Cache of events known to fail verification. */\nconst failedEvents = new WeakSet<Event>();\n\n/** Cache of object identities that already passed {@link validateSignedEvent}. */\nconst validatedEvents = new WeakSet<Record<string, unknown>>();\n\nexport function markVerified(event: Event): void {\n verifiedEvents.add(event);\n failedEvents.delete(event);\n}\n\nexport function markUnverified(event: Event): void {\n failedEvents.add(event);\n verifiedEvents.delete(event);\n}\n\nexport function isMarkedVerified(event: Event): boolean {\n return verifiedEvents.has(event);\n}\n\nexport function isMarkedFailed(event: Event): boolean {\n return failedEvents.has(event);\n}\n\n/** Structural validation of an unsigned event (no crypto). */\nexport function validateEvent(event: unknown): event is UnsignedEvent {\n if (!isRecord(event)) {\n return false;\n }\n if (\n typeof event[\"kind\"] !== \"number\" ||\n !Number.isSafeInteger(event[\"kind\"]) ||\n event[\"kind\"] < 0 ||\n event[\"kind\"] > 65535\n ) {\n return false;\n }\n // Lone surrogates are rejected per NIP-01: after JSON.parse they differ from\n // what serde_json (and thus Rust) can express, so the event id would diverge.\n if (typeof event[\"content\"] !== \"string\" || hasLoneSurrogate(event[\"content\"])) {\n return false;\n }\n if (\n typeof event[\"created_at\"] !== \"number\" ||\n !Number.isSafeInteger(event[\"created_at\"]) ||\n event[\"created_at\"] < 0\n ) {\n return false;\n }\n if (typeof event[\"pubkey\"] !== \"string\" || !isHex32(event[\"pubkey\"])) {\n return false;\n }\n if (!Array.isArray(event[\"tags\"])) {\n return false;\n }\n for (const tag of event[\"tags\"]) {\n if (!isTag(tag)) {\n return false;\n }\n for (const item of tag) {\n if (hasLoneSurrogate(item)) {\n return false;\n }\n }\n }\n return true;\n}\n\n/** Structural validation of a full event including id and sig hex shape. */\nexport function validateSignedEvent(event: unknown): event is Event {\n if (isRecord(event) && validatedEvents.has(event)) {\n return true;\n }\n if (!validateEvent(event)) {\n return false;\n }\n const e = event as Record<string, unknown>;\n if (typeof e[\"id\"] !== \"string\" || !isHex32(e[\"id\"])) {\n return false;\n }\n if (typeof e[\"sig\"] !== \"string\" || !isHex64(e[\"sig\"])) {\n return false;\n }\n validatedEvents.add(e);\n return true;\n}\n\n/**\n * Canonical NIP-01 serialization used for hashing: JSON array `[0, pubkey, created_at, kind, tags,\n * content]` with no extra whitespace.\n */\nexport function serializeEvent(event: UnsignedEvent): string {\n if (!validateEvent(event)) {\n throw new EventValidationError(\"cannot serialize event with invalid shape\");\n }\n return serializeValidatedEvent(event);\n}\n\n/**\n * Canonical serialization for an event already known valid (after {@link validateEvent} or\n * {@link validateSignedEvent}); identical output to {@link serializeEvent} without re-walking the\n * structural checks.\n */\nexport function serializeValidatedEvent(event: UnsignedEvent): string {\n return JSON.stringify([0, event.pubkey, event.created_at, event.kind, event.tags, event.content]);\n}\n\n/** SHA-256 of the canonical serialization, lowercase hex. */\nexport function getEventHash(event: UnsignedEvent): string {\n const serialized = serializeEvent(event);\n return bytesToHex(sha256(utf8Encoder.encode(serialized)));\n}\n\n/** Id tie-break: plain code-unit order (ids are canonical lowercase hex, so `<` is lexical). */\nfunction compareIds(a: { id: string }, b: { id: string }): number {\n if (a.id < b.id) {\n return -1;\n }\n if (a.id > b.id) {\n return 1;\n }\n return 0;\n}\n\n/**\n * NIP-01 newest-first order: `created_at` descending, then `id` in plain code-unit order (ids are\n * canonical lowercase hex, so `<` is lexical order).\n */\nexport function compareEventsDesc(\n a: { id: string; created_at: number },\n b: { id: string; created_at: number },\n): number {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at;\n }\n return compareIds(a, b);\n}\n\n/**\n * Sort events reverse-chronologically by `created_at`, then by `id` lexicographically. Mutates and\n * returns the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort(compareEventsDesc);\n}\n\n/** Non-mutating sort; returns a new array. */\nexport function sortedEvents(events: ReadonlyArray<Event>): Event[] {\n return [...events].sort(compareEventsDesc);\n}\n\n/**\n * Negentropy item order: `created_at` ascending, then `id` lexicographic (`<`/`>`). Time axis is\n * opposite {@link sortEvents}; id tie-break is the same direction.\n */\nexport function itemCompare(\n a: { id: string; created_at: number },\n b: { id: string; created_at: number },\n): number {\n if (a.created_at !== b.created_at) {\n return a.created_at - b.created_at;\n }\n return compareIds(a, b);\n}\n\n/**\n * NIP-01 replaceable/addressable winner: higher `created_at`, or equal timestamp and\n * lexicographically lower `id`.\n */\nexport function isReplaceableWinner(\n candidate: { created_at: number; id: string },\n incumbent: { created_at: number; id: string },\n): boolean {\n if (candidate.created_at !== incumbent.created_at) {\n return candidate.created_at > incumbent.created_at;\n }\n return candidate.id < incumbent.id;\n}\n\n/** True when a signed event is the signed form of `unsigned` (kind/tags/content/time/pubkey). */\nexport function signedMatchesUnsigned(signed: Event, unsigned: UnsignedEvent): boolean {\n if (signed.kind !== unsigned.kind) {\n return false;\n }\n if (signed.content !== unsigned.content) {\n return false;\n }\n if (signed.created_at !== unsigned.created_at) {\n return false;\n }\n if (JSON.stringify(signed.tags) !== JSON.stringify(unsigned.tags)) {\n return false;\n }\n if (unsigned.pubkey && signed.pubkey.toLowerCase() !== unsigned.pubkey.toLowerCase()) {\n return false;\n }\n return true;\n}\n"],"mappings":"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"}
@@ -1,27 +1,57 @@
1
- import { o as isReplaceableKind, r as isAddressableKind } from "./kind-D8fENiiL.mjs";
1
+ import { o as isReplaceableKind, r as isAddressableKind } from "./kind-I2Tvbe82.mjs";
2
2
  //#region src/core/filter.ts
3
3
  /** NIP-01 only defines single-letter tag conditions. */
4
4
  const SINGLE_LETTER_TAG = /^#[a-zA-Z]$/;
5
- /** Local NIP-01 match. `search` is ignored; relays interpret NIP-50. */
6
- function matchFilter(filter, event) {
7
- if (filter.ids && !filter.ids.some((id) => id.toLowerCase() === event.id)) return false;
8
- if (filter.kinds && !filter.kinds.includes(event.kind)) return false;
9
- if (filter.authors && !filter.authors.some((pk) => pk.toLowerCase() === event.pubkey)) return false;
5
+ const compiledFilters = /* @__PURE__ */ new WeakMap();
6
+ /**
7
+ * Single-letter `#x` conditions shared by `matchFilter` and the event stores: `#e`/`#p` values are
8
+ * lowercased (they hold hex ids, same as `encodeClientMessage`), other tag values stay verbatim —
9
+ * relays match tag values literally. Multi-letter `#` keys are outside NIP-01. Internal to the
10
+ * package; not exported from the index.
11
+ */
12
+ function filterTagConditions(filter) {
13
+ const conditions = [];
10
14
  for (const key of Object.keys(filter)) {
11
15
  if (!SINGLE_LETTER_TAG.test(key)) continue;
12
- const tagName = key.slice(1);
13
- const values = filter[`#${tagName}`];
14
- if (!values) continue;
15
- const hexTag = tagName === "e" || tagName === "p";
16
- if (!event.tags.some((tag) => {
17
- const [tagKey, tagValue] = tag;
18
- if (tagKey !== tagName || tagValue === void 0) return false;
19
- if (hexTag) return values.some((v) => v.toLowerCase() === tagValue.toLowerCase());
20
- return values.includes(tagValue);
21
- })) return false;
16
+ const name = key.slice(1);
17
+ const values = filter[`#${name}`];
18
+ if (values === void 0) continue;
19
+ conditions.push({
20
+ name,
21
+ values: name === "e" || name === "p" ? values.map((v) => v.toLowerCase()) : values
22
+ });
23
+ }
24
+ return conditions;
25
+ }
26
+ function compileFilter(filter) {
27
+ return {
28
+ ids: filter.ids ? new Set(filter.ids.map((id) => id.toLowerCase())) : void 0,
29
+ kinds: filter.kinds ? new Set(filter.kinds) : void 0,
30
+ authors: filter.authors ? new Set(filter.authors.map((pk) => pk.toLowerCase())) : void 0,
31
+ tags: filterTagConditions(filter).map(({ name, values }) => ({
32
+ name,
33
+ values: new Set(values)
34
+ })),
35
+ since: filter.since,
36
+ until: filter.until
37
+ };
38
+ }
39
+ /** Local NIP-01 match. `search` is ignored; relays interpret NIP-50. */
40
+ function matchFilter(filter, event) {
41
+ let compiled = compiledFilters.get(filter);
42
+ if (compiled === void 0) {
43
+ compiled = compileFilter(filter);
44
+ compiledFilters.set(filter, compiled);
22
45
  }
23
- if (filter.since !== void 0 && event.created_at < filter.since) return false;
24
- if (filter.until !== void 0 && event.created_at > filter.until) return false;
46
+ if (compiled.ids !== void 0 && !compiled.ids.has(event.id)) return false;
47
+ if (compiled.kinds !== void 0 && !compiled.kinds.has(event.kind)) return false;
48
+ if (compiled.authors !== void 0 && !compiled.authors.has(event.pubkey)) return false;
49
+ for (const { name, values } of compiled.tags) if (!event.tags.some((tag) => {
50
+ const [tagKey, tagValue] = tag;
51
+ return tagKey === name && tagValue !== void 0 && values.has(tagValue);
52
+ })) return false;
53
+ if (compiled.since !== void 0 && event.created_at < compiled.since) return false;
54
+ if (compiled.until !== void 0 && event.created_at > compiled.until) return false;
25
55
  return true;
26
56
  }
27
57
  /** True when the event matches any of the filters (NIP-01 OR semantics). */
@@ -103,6 +133,6 @@ function filterFingerprint(filters) {
103
133
  return `[${parts.join(",")}]`;
104
134
  }
105
135
  //#endregion
106
- export { matchFilter as a, getFilterLimit as i, canonicalizeFilters as n, matchFilters as o, filterFingerprint as r, canonicalizeFilter as t };
136
+ export { getFilterLimit as a, filterTagConditions as i, canonicalizeFilters as n, matchFilter as o, filterFingerprint as r, matchFilters as s, canonicalizeFilter as t };
107
137
 
108
- //# sourceMappingURL=filter-B16P33dz.mjs.map
138
+ //# sourceMappingURL=filter-3jOh4uLN.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-3jOh4uLN.mjs","names":[],"sources":["../src/core/filter.ts"],"sourcesContent":["import type { Event } from \"./event.ts\";\nimport { isAddressableKind, isReplaceableKind } from \"./kind.ts\";\n\n/**\n * NIP-01 filter object (immutable-friendly; callers may still pass plain objects). Filters are\n * treated as immutable once used: `matchFilter` and the event stores cache a compiled form keyed on\n * the object, so mutating a filter after it has been matched yields stale results.\n */\nexport type Filter = {\n readonly ids?: ReadonlyArray<string> | undefined;\n readonly kinds?: ReadonlyArray<number> | undefined;\n readonly authors?: ReadonlyArray<string> | undefined;\n readonly since?: number | undefined;\n readonly until?: number | undefined;\n readonly limit?: number | undefined;\n /** NIP-50 full-text search. Relays interpret this. Local `matchFilter` / `query` ignore it. */\n readonly search?: string | undefined;\n readonly [key: `#${string}`]: ReadonlyArray<string> | undefined;\n};\n\n/** NIP-01 only defines single-letter tag conditions. */\nconst SINGLE_LETTER_TAG = /^#[a-zA-Z]$/;\n\ntype CompiledTagCondition = {\n readonly name: string;\n readonly values: ReadonlySet<string>;\n};\n\n/** `matchFilter` condition form: `Set` lookups instead of per-element scans and `toLowerCase`. */\ntype CompiledFilter = {\n readonly ids: ReadonlySet<string> | undefined;\n readonly kinds: ReadonlySet<number> | undefined;\n readonly authors: ReadonlySet<string> | undefined;\n readonly tags: ReadonlyArray<CompiledTagCondition>;\n readonly since: number | undefined;\n readonly until: number | undefined;\n};\n\nconst compiledFilters = new WeakMap<Filter, CompiledFilter>();\n\n/**\n * Single-letter `#x` conditions shared by `matchFilter` and the event stores: `#e`/`#p` values are\n * lowercased (they hold hex ids, same as `encodeClientMessage`), other tag values stay verbatim —\n * relays match tag values literally. Multi-letter `#` keys are outside NIP-01. Internal to the\n * package; not exported from the index.\n */\nexport function filterTagConditions(\n filter: Filter,\n): Array<{ name: string; values: ReadonlyArray<string> }> {\n const conditions: Array<{ name: string; values: ReadonlyArray<string> }> = [];\n for (const key of Object.keys(filter)) {\n if (!SINGLE_LETTER_TAG.test(key)) {\n continue;\n }\n const name = key.slice(1);\n const values = filter[`#${name}`];\n if (values === undefined) {\n continue;\n }\n conditions.push({\n name,\n values: name === \"e\" || name === \"p\" ? values.map((v) => v.toLowerCase()) : values,\n });\n }\n return conditions;\n}\n\nfunction compileFilter(filter: Filter): CompiledFilter {\n return {\n ids: filter.ids ? new Set(filter.ids.map((id) => id.toLowerCase())) : undefined,\n kinds: filter.kinds ? new Set(filter.kinds) : undefined,\n authors: filter.authors ? new Set(filter.authors.map((pk) => pk.toLowerCase())) : undefined,\n tags: filterTagConditions(filter).map(({ name, values }) => ({\n name,\n values: new Set(values),\n })),\n since: filter.since,\n until: filter.until,\n };\n}\n\n/** Local NIP-01 match. `search` is ignored; relays interpret NIP-50. */\nexport function matchFilter(filter: Filter, event: Event): boolean {\n let compiled = compiledFilters.get(filter);\n if (compiled === undefined) {\n compiled = compileFilter(filter);\n compiledFilters.set(filter, compiled);\n }\n if (compiled.ids !== undefined && !compiled.ids.has(event.id)) {\n return false;\n }\n if (compiled.kinds !== undefined && !compiled.kinds.has(event.kind)) {\n return false;\n }\n if (compiled.authors !== undefined && !compiled.authors.has(event.pubkey)) {\n return false;\n }\n\n for (const { name, values } of compiled.tags) {\n const hit = event.tags.some((tag) => {\n const [tagKey, tagValue] = tag;\n return tagKey === name && tagValue !== undefined && values.has(tagValue);\n });\n if (!hit) {\n return false;\n }\n }\n\n if (compiled.since !== undefined && event.created_at < compiled.since) {\n return false;\n }\n if (compiled.until !== undefined && event.created_at > compiled.until) {\n return false;\n }\n\n return true;\n}\n\n/** True when the event matches any of the filters (NIP-01 OR semantics). */\nexport function matchFilters(filters: ReadonlyArray<Filter>, event: Event): boolean {\n for (const filter of filters) {\n if (matchFilter(filter, event)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Intrinsic upper bound implied by the filter alone. Returns a positive integer, or `Infinity` when\n * unbounded.\n */\nexport function getFilterLimit(filter: Filter): number {\n if (filter.ids && filter.ids.length === 0) {\n return 0;\n }\n if (filter.kinds && filter.kinds.length === 0) {\n return 0;\n }\n if (filter.authors && filter.authors.length === 0) {\n return 0;\n }\n\n let limit = Number.POSITIVE_INFINITY;\n if (filter.ids) {\n limit = Math.min(limit, new Set(filter.ids.map((id) => id.toLowerCase())).size);\n }\n if (filter.limit !== undefined) {\n limit = Math.min(limit, filter.limit);\n }\n\n if (filter.kinds && filter.authors) {\n const kinds = new Set(filter.kinds);\n const allReplaceable = [...kinds].every((k) => isReplaceableKind(k) || isAddressableKind(k));\n if (allReplaceable) {\n const dTags = filter[\"#d\"];\n const perAuthor = dTags && dTags.length > 0 ? new Set(dTags).size : 1;\n const authors = new Set(filter.authors.map((pk) => pk.toLowerCase())).size;\n limit = Math.min(limit, authors * kinds.size * perAuthor);\n }\n }\n\n return limit;\n}\n\nconst HEX_LIST_KEYS = new Set([\"ids\", \"authors\", \"#e\", \"#p\"]);\n\n/** NIP-01 known fields; everything else (incl. multi-letter `#` keys) is dropped. */\nconst KNOWN_FILTER_KEYS = new Set([\"ids\", \"authors\", \"kinds\", \"since\", \"until\", \"limit\", \"search\"]);\n\n/**\n * Lowercase hex lists, sort and dedupe every array, and keep only the NIP-01 known fields plus\n * single-letter `#` keys — unknown keys and multi-letter `#` keys are dropped. Omits undefined so\n * `[]` stays distinct from missing.\n */\nexport function canonicalizeFilter(filter: Filter): Filter {\n const raw = filter as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(raw).sort()) {\n if (key.startsWith(\"#\")) {\n if (!SINGLE_LETTER_TAG.test(key)) {\n continue;\n }\n } else if (!KNOWN_FILTER_KEYS.has(key)) {\n continue;\n }\n const value = raw[key];\n // omit undefined so a missing key is not `[]` / null\n if (value === undefined) {\n continue;\n }\n if (Array.isArray(value)) {\n if (HEX_LIST_KEYS.has(key)) {\n out[key] = [...new Set(value.map((v) => String(v).toLowerCase()))].sort();\n } else if (key === \"kinds\") {\n out[key] = [...new Set(value.map(Number))].sort((a, b) => a - b);\n } else {\n out[key] = [...new Set(value.map(String))].sort();\n }\n } else {\n out[key] = value;\n }\n }\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- built key by key above; the record is filter-shaped by construction\n return out as Filter;\n}\n\n/** Apply {@link canonicalizeFilter} to each filter. */\nexport function canonicalizeFilters(filters: ReadonlyArray<Filter>): Filter[] {\n return filters.map(canonicalizeFilter);\n}\n\n/**\n * Canonical identity for live REQ coalescing. Relays treat a missing list key as unconstrained and\n * `[]` as match-nothing.\n */\nexport function filterFingerprint(filters: ReadonlyArray<Filter>): string {\n const parts = filters.map((filter) => JSON.stringify(canonicalizeFilter(filter)));\n parts.sort();\n return `[${parts.join(\",\")}]`;\n}\n"],"mappings":"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"}
@@ -1,6 +1,10 @@
1
1
  import { t as Event } from "./event-j3BwI5dF.mjs";
2
2
  //#region src/core/filter.d.ts
3
- /** NIP-01 filter object (immutable-friendly; callers may still pass plain objects). */
3
+ /**
4
+ * NIP-01 filter object (immutable-friendly; callers may still pass plain objects). Filters are
5
+ * treated as immutable once used: `matchFilter` and the event stores cache a compiled form keyed on
6
+ * the object, so mutating a filter after it has been matched yields stale results.
7
+ */
4
8
  type Filter = {
5
9
  readonly ids?: ReadonlyArray<string> | undefined;
6
10
  readonly kinds?: ReadonlyArray<number> | undefined;
@@ -36,4 +40,4 @@ declare function canonicalizeFilters(filters: ReadonlyArray<Filter>): Filter[];
36
40
  declare function filterFingerprint(filters: ReadonlyArray<Filter>): string;
37
41
  //#endregion
38
42
  export { getFilterLimit as a, filterFingerprint as i, canonicalizeFilter as n, matchFilter as o, canonicalizeFilters as r, matchFilters as s, Filter as t };
39
- //# sourceMappingURL=filter-PG7r79rt.d.mts.map
43
+ //# sourceMappingURL=filter-oYG2zjJ3.d.mts.map
@@ -1,6 +1,6 @@
1
1
  import { t as Event } from "./event-j3BwI5dF.mjs";
2
- import { t as Filter } from "./filter-PG7r79rt.mjs";
3
- import { t as RelayListItem } from "./nip65-DaWMg943.mjs";
2
+ import { t as Filter } from "./filter-oYG2zjJ3.mjs";
3
+ import { t as RelayListItem } from "./nip65-CPNE0SV0.mjs";
4
4
  //#region src/gossip/gossip.d.ts
5
5
  /** Relay routing state for one pubkey: NIP-65 outbox/inbox plus NIP-17 DM relays. */
6
6
  type PubkeyRoutes = {
@@ -82,4 +82,4 @@ declare class Gossip {
82
82
  }
83
83
  //#endregion
84
84
  export { RoutedFilter as i, GossipOptions as n, PubkeyRoutes as r, Gossip as t };
85
- //# sourceMappingURL=gossip-u0iBlZ1U.d.mts.map
85
+ //# sourceMappingURL=gossip-BvCItMVZ.d.mts.map
@@ -1,6 +1,6 @@
1
- import { d as normalizeURL, f as nowSeconds, u as normalizeRelayUrls } from "./util-DDpPkiED.mjs";
2
- import { t as Kind } from "./kind-D8fENiiL.mjs";
3
- import { a as isReplaceableWinner } from "./event-B3ua2_oL.mjs";
1
+ import { d as normalizeRelayUrls, f as normalizeURL, p as nowSeconds } from "./util-DfsyJ9nz.mjs";
2
+ import { t as Kind } from "./kind-I2Tvbe82.mjs";
3
+ import { a as isReplaceableWinner } from "./event-DvM1f03b.mjs";
4
4
  import { parseRelayList } from "./nips/nip65.mjs";
5
5
  import { i as trimOldest, r as touchKey } from "./collections-DtwMc1NU.mjs";
6
6
  import { parseDmRelayList } from "./nips/nip17.mjs";
@@ -225,4 +225,4 @@ var Gossip = class {
225
225
  //#endregion
226
226
  export { Gossip as t };
227
227
 
228
- //# sourceMappingURL=gossip-K_JIexQs.mjs.map
228
+ //# sourceMappingURL=gossip-CRyhK6E6.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"gossip-K_JIexQs.mjs","names":[],"sources":["../src/gossip/gossip.ts"],"sourcesContent":["import { touchKey, trimOldest } from \"../core/collections.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { isReplaceableWinner } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { normalizeRelayUrls, normalizeURL, nowSeconds } from \"../core/util.ts\";\nimport { parseDmRelayList } from \"../nips/nip17.ts\";\nimport { parseRelayList } from \"../nips/nip65.ts\";\nimport type { RelayListItem } from \"../nips/nip65.ts\";\n\n/** Relay routing state for one pubkey: NIP-65 outbox/inbox plus NIP-17 DM relays. */\nexport type PubkeyRoutes = {\n /** Relays the user writes to (outbox). NIP-65. */\n readonly write: ReadonlyArray<string>;\n /** Relays the user reads from (inbox). NIP-65. */\n readonly read: ReadonlyArray<string>;\n /** Relays for NIP-17 gift-wrap delivery. Kind 10050. */\n readonly dm: ReadonlyArray<string>;\n /** `created_at` of the last accepted kind:10002 list. */\n readonly updatedAt: number;\n /** `created_at` of the last accepted kind:10050 list. */\n readonly dmUpdatedAt: number;\n /** Event id of the last accepted kind:10002 (NIP-01 equal-timestamp tie-break). */\n readonly relayListId?: string | undefined;\n /** Event id of the last accepted kind:10050. */\n readonly dmListId?: string | undefined;\n};\n\n/** A filter split by gossip routes: per-relay narrowed filters plus the unrouted remainder. */\nexport type RoutedFilter = {\n /** Url → already-narrowed filter. Empty when nothing routed. */\n readonly perRelay: ReadonlyMap<string, Filter>;\n /**\n * Unrouted work for the caller:\n *\n * - No authors and no #p: original filter (\"generic\")\n * - Authors/#p all unrouted: original filter (\"orphan\")\n * - Mixed: narrowed leftover (\"fallback\")\n * - All routed: undefined\n */\n readonly remainder?: Filter | undefined;\n};\n\nexport type GossipOptions = {\n /**\n * Max relays kept per direction; the first N in list order win (NIP-65 defines no ranking).\n * Default 4.\n */\n maxRelaysPerPubkey?: number | undefined;\n /**\n * Max pubkeys with tracked routes; the map is LRU — writes and lookups refresh recency, and\n * inserting beyond the cap drops the oldest entry. Default 10_000.\n */\n maxPubkeys?: number | undefined;\n};\n\nfunction emptyRoutes(): PubkeyRoutes {\n return {\n write: [],\n read: [],\n dm: [],\n updatedAt: 0,\n dmUpdatedAt: 0,\n };\n}\n\n/**\n * Routing table for NIP-65 (10002) and NIP-17 DM relays (10050). Ingest replaceable list events,\n * then route filters into per-relay REQs.\n */\nexport class Gossip {\n readonly #routes = new Map<string, PubkeyRoutes>();\n readonly #maxRelays: number;\n readonly #maxPubkeys: number;\n\n constructor(opts: GossipOptions = {}) {\n this.#maxRelays = opts.maxRelaysPerPubkey ?? 4;\n this.#maxPubkeys = opts.maxPubkeys ?? 10_000;\n }\n\n /**\n * Ingest a routing list event.\n *\n * - Kind:10002 → write/read (NIP-65)\n * - Kind:10050 → dm (NIP-17)\n *\n * Returns true if routes for that list type were updated.\n */\n ingest(event: Event): boolean {\n if (event.kind === Kind.RelayList) {\n let items: RelayListItem[];\n try {\n items = parseRelayList(event);\n } catch {\n return false;\n }\n return this.setRoutes(event.pubkey, items, event.created_at, event.id);\n }\n if (event.kind === Kind.DirectMessageRelaysList) {\n let relays: string[];\n try {\n relays = parseDmRelayList(event);\n } catch {\n return false;\n }\n return this.setDmRoutes(event.pubkey, relays, event.created_at, event.id);\n }\n return false;\n }\n\n setRoutes(\n pubkey: string,\n items: ReadonlyArray<RelayListItem>,\n updatedAt: number = nowSeconds(),\n eventId?: string,\n ): boolean {\n const pk = pubkey.toLowerCase();\n const prev = this.#lookup(pk) ?? emptyRoutes();\n if (prev.updatedAt > updatedAt) {\n return false;\n }\n if (\n eventId !== undefined &&\n prev.relayListId !== undefined &&\n prev.updatedAt === updatedAt &&\n !isReplaceableWinner(\n { created_at: updatedAt, id: eventId },\n { created_at: prev.updatedAt, id: prev.relayListId },\n )\n ) {\n return false;\n }\n\n const write: string[] = [];\n const read: string[] = [];\n for (const item of items) {\n let url: string;\n try {\n url = normalizeURL(item.url);\n } catch {\n continue;\n }\n if (item.marker !== \"read\" && !write.includes(url)) {\n write.push(url);\n }\n if (item.marker !== \"write\" && !read.includes(url)) {\n read.push(url);\n }\n }\n\n this.#put(pk, {\n write: write.slice(0, this.#maxRelays),\n read: read.slice(0, this.#maxRelays),\n dm: prev.dm,\n updatedAt,\n dmUpdatedAt: prev.dmUpdatedAt,\n relayListId: eventId ?? prev.relayListId,\n dmListId: prev.dmListId,\n });\n return true;\n }\n\n setDmRoutes(\n pubkey: string,\n relays: ReadonlyArray<string>,\n updatedAt: number = nowSeconds(),\n eventId?: string,\n ): boolean {\n const pk = pubkey.toLowerCase();\n const prev = this.#lookup(pk) ?? emptyRoutes();\n if (prev.dmUpdatedAt > updatedAt) {\n return false;\n }\n if (\n eventId !== undefined &&\n prev.dmListId !== undefined &&\n prev.dmUpdatedAt === updatedAt &&\n !isReplaceableWinner(\n { created_at: updatedAt, id: eventId },\n { created_at: prev.dmUpdatedAt, id: prev.dmListId },\n )\n ) {\n return false;\n }\n\n const dm = normalizeRelayUrls(relays);\n\n this.#put(pk, {\n write: prev.write,\n read: prev.read,\n dm: dm.slice(0, this.#maxRelays),\n updatedAt: prev.updatedAt,\n dmUpdatedAt: updatedAt,\n relayListId: prev.relayListId,\n dmListId: eventId ?? prev.dmListId,\n });\n return true;\n }\n\n getRoutes(pubkey: string): Readonly<PubkeyRoutes> | undefined {\n return this.#lookup(pubkey.toLowerCase());\n }\n\n outboxRelays(pubkey: string): ReadonlyArray<string> {\n return this.#lookup(pubkey.toLowerCase())?.write ?? [];\n }\n\n inboxRelays(pubkey: string): ReadonlyArray<string> {\n return this.#lookup(pubkey.toLowerCase())?.read ?? [];\n }\n\n /** NIP-17 kind:10050 delivery relays for gift-wraps. */\n dmRelays(pubkey: string): ReadonlyArray<string> {\n return this.#lookup(pubkey.toLowerCase())?.dm ?? [];\n }\n\n clear(pubkey?: string): void {\n if (pubkey !== undefined && pubkey !== \"\") {\n this.#routes.delete(pubkey.toLowerCase());\n } else {\n this.#routes.clear();\n }\n }\n\n get size(): number {\n return this.#routes.size;\n }\n\n /**\n * Route a user-facing filter into per-relay sub-filters plus leftover.\n *\n * - Authors → outbox relays, filter narrowed per relay's authors\n * - #p only → inbox relays of those pubkeys\n * - Neither / all unrouted → remainder is the original filter\n * - Mixed → remainder is the narrowed leftover; all routed → no remainder\n */\n route(filter: Filter): RoutedFilter {\n const authors = filter.authors?.map((a) => a.toLowerCase());\n const pTags = filter[\"#p\"]?.map((p) => p.toLowerCase());\n\n if ((!authors || authors.length === 0) && (!pTags || pTags.length === 0)) {\n return { perRelay: new Map(), remainder: filter };\n }\n\n if (authors && authors.length > 0 && (!pTags || pTags.length === 0)) {\n return this.#breakAuthors(filter, authors, \"write\");\n }\n\n if (pTags && pTags.length > 0 && (!authors || authors.length === 0)) {\n const { authors: _authors, ...base } = filter;\n return this.#breakAuthors(base, pTags, \"read\", (b, pubkeys) => ({ ...b, \"#p\": pubkeys }));\n }\n\n // both authors and #p: send full filter to union of routes\n const unionKeys = new Set([...(authors ?? []), ...(pTags ?? [])]);\n const relays = new Set<string>();\n let anyUnrouted = false;\n for (const pk of unionKeys) {\n const r = this.#lookup(pk);\n const urls = r ? [...r.write, ...r.read] : [];\n if (urls.length === 0) {\n anyUnrouted = true;\n continue;\n }\n for (const u of urls) {\n relays.add(u);\n }\n }\n if (relays.size === 0) {\n return { perRelay: new Map(), remainder: filter };\n }\n const map = new Map<string, Filter>();\n for (const url of relays) {\n map.set(url, { ...filter });\n }\n return anyUnrouted ? { perRelay: map, remainder: filter } : { perRelay: map };\n }\n\n #breakAuthors(\n filter: Filter,\n pubkeys: string[],\n direction: \"read\" | \"write\",\n narrow: (base: Filter, pubkeys: string[]) => Filter = (base, pks) => ({\n ...base,\n authors: pks,\n }),\n ): RoutedFilter {\n const perRelay = new Map<string, string[]>();\n const unrouted: string[] = [];\n let anyRoute = false;\n\n for (const pk of pubkeys) {\n const routes = this.#lookup(pk);\n const urls = direction === \"write\" ? routes?.write : routes?.read;\n if (!urls || urls.length === 0) {\n unrouted.push(pk);\n continue;\n }\n anyRoute = true;\n for (const url of urls) {\n const list = perRelay.get(url) ?? [];\n list.push(pk);\n perRelay.set(url, list);\n }\n }\n\n if (!anyRoute) {\n return { perRelay: new Map(), remainder: filter };\n }\n\n const map = new Map<string, Filter>();\n for (const [url, pks] of perRelay) {\n map.set(url, narrow(filter, pks));\n }\n return unrouted.length > 0\n ? { perRelay: map, remainder: narrow(filter, unrouted) }\n : { perRelay: map };\n }\n\n /** Read that refreshes LRU recency. */\n #lookup(pk: string): PubkeyRoutes | undefined {\n const routes = this.#routes.get(pk);\n if (routes !== undefined) {\n touchKey(this.#routes, pk, routes);\n }\n return routes;\n }\n\n /** Write at the newest LRU end, then trim the oldest beyond the cap. */\n #put(pk: string, routes: PubkeyRoutes): void {\n touchKey(this.#routes, pk, routes);\n trimOldest(this.#routes, this.#maxPubkeys);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gossip-CRyhK6E6.mjs","names":[],"sources":["../src/gossip/gossip.ts"],"sourcesContent":["import { touchKey, trimOldest } from \"../core/collections.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { isReplaceableWinner } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { normalizeRelayUrls, normalizeURL, nowSeconds } from \"../core/util.ts\";\nimport { parseDmRelayList } from \"../nips/nip17.ts\";\nimport { parseRelayList } from \"../nips/nip65.ts\";\nimport type { RelayListItem } from \"../nips/nip65.ts\";\n\n/** Relay routing state for one pubkey: NIP-65 outbox/inbox plus NIP-17 DM relays. */\nexport type PubkeyRoutes = {\n /** Relays the user writes to (outbox). NIP-65. */\n readonly write: ReadonlyArray<string>;\n /** Relays the user reads from (inbox). NIP-65. */\n readonly read: ReadonlyArray<string>;\n /** Relays for NIP-17 gift-wrap delivery. Kind 10050. */\n readonly dm: ReadonlyArray<string>;\n /** `created_at` of the last accepted kind:10002 list. */\n readonly updatedAt: number;\n /** `created_at` of the last accepted kind:10050 list. */\n readonly dmUpdatedAt: number;\n /** Event id of the last accepted kind:10002 (NIP-01 equal-timestamp tie-break). */\n readonly relayListId?: string | undefined;\n /** Event id of the last accepted kind:10050. */\n readonly dmListId?: string | undefined;\n};\n\n/** A filter split by gossip routes: per-relay narrowed filters plus the unrouted remainder. */\nexport type RoutedFilter = {\n /** Url → already-narrowed filter. Empty when nothing routed. */\n readonly perRelay: ReadonlyMap<string, Filter>;\n /**\n * Unrouted work for the caller:\n *\n * - No authors and no #p: original filter (\"generic\")\n * - Authors/#p all unrouted: original filter (\"orphan\")\n * - Mixed: narrowed leftover (\"fallback\")\n * - All routed: undefined\n */\n readonly remainder?: Filter | undefined;\n};\n\nexport type GossipOptions = {\n /**\n * Max relays kept per direction; the first N in list order win (NIP-65 defines no ranking).\n * Default 4.\n */\n maxRelaysPerPubkey?: number | undefined;\n /**\n * Max pubkeys with tracked routes; the map is LRU — writes and lookups refresh recency, and\n * inserting beyond the cap drops the oldest entry. Default 10_000.\n */\n maxPubkeys?: number | undefined;\n};\n\nfunction emptyRoutes(): PubkeyRoutes {\n return {\n write: [],\n read: [],\n dm: [],\n updatedAt: 0,\n dmUpdatedAt: 0,\n };\n}\n\n/**\n * Routing table for NIP-65 (10002) and NIP-17 DM relays (10050). Ingest replaceable list events,\n * then route filters into per-relay REQs.\n */\nexport class Gossip {\n readonly #routes = new Map<string, PubkeyRoutes>();\n readonly #maxRelays: number;\n readonly #maxPubkeys: number;\n\n constructor(opts: GossipOptions = {}) {\n this.#maxRelays = opts.maxRelaysPerPubkey ?? 4;\n this.#maxPubkeys = opts.maxPubkeys ?? 10_000;\n }\n\n /**\n * Ingest a routing list event.\n *\n * - Kind:10002 → write/read (NIP-65)\n * - Kind:10050 → dm (NIP-17)\n *\n * Returns true if routes for that list type were updated.\n */\n ingest(event: Event): boolean {\n if (event.kind === Kind.RelayList) {\n let items: RelayListItem[];\n try {\n items = parseRelayList(event);\n } catch {\n return false;\n }\n return this.setRoutes(event.pubkey, items, event.created_at, event.id);\n }\n if (event.kind === Kind.DirectMessageRelaysList) {\n let relays: string[];\n try {\n relays = parseDmRelayList(event);\n } catch {\n return false;\n }\n return this.setDmRoutes(event.pubkey, relays, event.created_at, event.id);\n }\n return false;\n }\n\n setRoutes(\n pubkey: string,\n items: ReadonlyArray<RelayListItem>,\n updatedAt: number = nowSeconds(),\n eventId?: string,\n ): boolean {\n const pk = pubkey.toLowerCase();\n const prev = this.#lookup(pk) ?? emptyRoutes();\n if (prev.updatedAt > updatedAt) {\n return false;\n }\n if (\n eventId !== undefined &&\n prev.relayListId !== undefined &&\n prev.updatedAt === updatedAt &&\n !isReplaceableWinner(\n { created_at: updatedAt, id: eventId },\n { created_at: prev.updatedAt, id: prev.relayListId },\n )\n ) {\n return false;\n }\n\n const write: string[] = [];\n const read: string[] = [];\n for (const item of items) {\n let url: string;\n try {\n url = normalizeURL(item.url);\n } catch {\n continue;\n }\n if (item.marker !== \"read\" && !write.includes(url)) {\n write.push(url);\n }\n if (item.marker !== \"write\" && !read.includes(url)) {\n read.push(url);\n }\n }\n\n this.#put(pk, {\n write: write.slice(0, this.#maxRelays),\n read: read.slice(0, this.#maxRelays),\n dm: prev.dm,\n updatedAt,\n dmUpdatedAt: prev.dmUpdatedAt,\n relayListId: eventId ?? prev.relayListId,\n dmListId: prev.dmListId,\n });\n return true;\n }\n\n setDmRoutes(\n pubkey: string,\n relays: ReadonlyArray<string>,\n updatedAt: number = nowSeconds(),\n eventId?: string,\n ): boolean {\n const pk = pubkey.toLowerCase();\n const prev = this.#lookup(pk) ?? emptyRoutes();\n if (prev.dmUpdatedAt > updatedAt) {\n return false;\n }\n if (\n eventId !== undefined &&\n prev.dmListId !== undefined &&\n prev.dmUpdatedAt === updatedAt &&\n !isReplaceableWinner(\n { created_at: updatedAt, id: eventId },\n { created_at: prev.dmUpdatedAt, id: prev.dmListId },\n )\n ) {\n return false;\n }\n\n const dm = normalizeRelayUrls(relays);\n\n this.#put(pk, {\n write: prev.write,\n read: prev.read,\n dm: dm.slice(0, this.#maxRelays),\n updatedAt: prev.updatedAt,\n dmUpdatedAt: updatedAt,\n relayListId: prev.relayListId,\n dmListId: eventId ?? prev.dmListId,\n });\n return true;\n }\n\n getRoutes(pubkey: string): Readonly<PubkeyRoutes> | undefined {\n return this.#lookup(pubkey.toLowerCase());\n }\n\n outboxRelays(pubkey: string): ReadonlyArray<string> {\n return this.#lookup(pubkey.toLowerCase())?.write ?? [];\n }\n\n inboxRelays(pubkey: string): ReadonlyArray<string> {\n return this.#lookup(pubkey.toLowerCase())?.read ?? [];\n }\n\n /** NIP-17 kind:10050 delivery relays for gift-wraps. */\n dmRelays(pubkey: string): ReadonlyArray<string> {\n return this.#lookup(pubkey.toLowerCase())?.dm ?? [];\n }\n\n clear(pubkey?: string): void {\n if (pubkey !== undefined && pubkey !== \"\") {\n this.#routes.delete(pubkey.toLowerCase());\n } else {\n this.#routes.clear();\n }\n }\n\n get size(): number {\n return this.#routes.size;\n }\n\n /**\n * Route a user-facing filter into per-relay sub-filters plus leftover.\n *\n * - Authors → outbox relays, filter narrowed per relay's authors\n * - #p only → inbox relays of those pubkeys\n * - Neither / all unrouted → remainder is the original filter\n * - Mixed → remainder is the narrowed leftover; all routed → no remainder\n */\n route(filter: Filter): RoutedFilter {\n const authors = filter.authors?.map((a) => a.toLowerCase());\n const pTags = filter[\"#p\"]?.map((p) => p.toLowerCase());\n\n if ((!authors || authors.length === 0) && (!pTags || pTags.length === 0)) {\n return { perRelay: new Map(), remainder: filter };\n }\n\n if (authors && authors.length > 0 && (!pTags || pTags.length === 0)) {\n return this.#breakAuthors(filter, authors, \"write\");\n }\n\n if (pTags && pTags.length > 0 && (!authors || authors.length === 0)) {\n const { authors: _authors, ...base } = filter;\n return this.#breakAuthors(base, pTags, \"read\", (b, pubkeys) => ({ ...b, \"#p\": pubkeys }));\n }\n\n // both authors and #p: send full filter to union of routes\n const unionKeys = new Set([...(authors ?? []), ...(pTags ?? [])]);\n const relays = new Set<string>();\n let anyUnrouted = false;\n for (const pk of unionKeys) {\n const r = this.#lookup(pk);\n const urls = r ? [...r.write, ...r.read] : [];\n if (urls.length === 0) {\n anyUnrouted = true;\n continue;\n }\n for (const u of urls) {\n relays.add(u);\n }\n }\n if (relays.size === 0) {\n return { perRelay: new Map(), remainder: filter };\n }\n const map = new Map<string, Filter>();\n for (const url of relays) {\n map.set(url, { ...filter });\n }\n return anyUnrouted ? { perRelay: map, remainder: filter } : { perRelay: map };\n }\n\n #breakAuthors(\n filter: Filter,\n pubkeys: string[],\n direction: \"read\" | \"write\",\n narrow: (base: Filter, pubkeys: string[]) => Filter = (base, pks) => ({\n ...base,\n authors: pks,\n }),\n ): RoutedFilter {\n const perRelay = new Map<string, string[]>();\n const unrouted: string[] = [];\n let anyRoute = false;\n\n for (const pk of pubkeys) {\n const routes = this.#lookup(pk);\n const urls = direction === \"write\" ? routes?.write : routes?.read;\n if (!urls || urls.length === 0) {\n unrouted.push(pk);\n continue;\n }\n anyRoute = true;\n for (const url of urls) {\n const list = perRelay.get(url) ?? [];\n list.push(pk);\n perRelay.set(url, list);\n }\n }\n\n if (!anyRoute) {\n return { perRelay: new Map(), remainder: filter };\n }\n\n const map = new Map<string, Filter>();\n for (const [url, pks] of perRelay) {\n map.set(url, narrow(filter, pks));\n }\n return unrouted.length > 0\n ? { perRelay: map, remainder: narrow(filter, unrouted) }\n : { perRelay: map };\n }\n\n /** Read that refreshes LRU recency. */\n #lookup(pk: string): PubkeyRoutes | undefined {\n const routes = this.#routes.get(pk);\n if (routes !== undefined) {\n touchKey(this.#routes, pk, routes);\n }\n return routes;\n }\n\n /** Write at the newest LRU end, then trim the oldest beyond the cap. */\n #put(pk: string, routes: PubkeyRoutes): void {\n touchKey(this.#routes, pk, routes);\n trimOldest(this.#routes, this.#maxPubkeys);\n }\n}\n"],"mappings":"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"}
package/dist/gossip.d.mts CHANGED
@@ -1,3 +1,3 @@
1
- import { i as RoutedFilter, n as GossipOptions, r as PubkeyRoutes, t as Gossip } from "./gossip-u0iBlZ1U.mjs";
1
+ import { i as RoutedFilter, n as GossipOptions, r as PubkeyRoutes, t as Gossip } from "./gossip-BvCItMVZ.mjs";
2
2
  import "./index-CaxrMD1A.mjs";
3
3
  export { Gossip, type GossipOptions, type PubkeyRoutes, type RoutedFilter };
package/dist/gossip.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as Gossip } from "./gossip-K_JIexQs.mjs";
1
+ import { t as Gossip } from "./gossip-CRyhK6E6.mjs";
2
2
  export { Gossip };
@@ -1 +1 @@
1
- {"version":3,"file":"http-CuazNRIP.mjs","names":[],"sources":["../src/nips/http.ts"],"sourcesContent":["/**\n * Shared nips HTTP primitive. Not a pack entry (core has zero network). Callers never pass\n * `redirect`; sendManual always sets `\"manual\"`.\n */\n\nexport type ManualFetchInit = {\n method?: string;\n headers?: Record<string, string>;\n // oxlint-disable-next-line no-restricted-types -- mirrors RequestInit.body, which is nullable\n body?: Blob | FormData | string | null;\n signal?: AbortSignal | undefined;\n /**\n * Always `\"manual\"`: NIP-05, NIP-11 and Blossom responses must never be followed through\n * redirects. Adapters must forward this to their underlying fetch implementation.\n */\n redirect: \"manual\";\n};\n\nexport type ManualFetch = (\n url: string,\n init?: ManualFetchInit,\n) => Promise<{\n ok: boolean;\n status: number;\n // oxlint-disable-next-line no-restricted-types -- mirrors Headers.get, which returns null\n headers: { get: (name: string) => string | null };\n json: () => Promise<unknown>;\n arrayBuffer: () => Promise<ArrayBuffer>;\n}>;\n\ntype ManualInit = Omit<ManualFetchInit, \"redirect\">;\n\nexport function requireGlobalFetch(missing: () => Error): ManualFetch {\n if (typeof globalThis.fetch !== \"function\") {\n throw missing();\n }\n const fetchImpl = globalThis.fetch.bind(globalThis);\n // RequestInit.signal is AbortSignal | null and rejects explicit undefined under\n // exactOptionalPropertyTypes; normalize it for the real fetch.\n return async (url, init) =>\n fetchImpl(url, init === undefined ? init : { ...init, signal: init.signal ?? null });\n}\n\n/** Always sets redirect:manual. Shared by fetchManual and headStatus. */\nexport async function sendManual(\n fetchImpl: ManualFetch,\n url: string,\n init: ManualInit,\n): Promise<Awaited<ReturnType<ManualFetch>>> {\n return fetchImpl(url, { ...init, redirect: \"manual\" });\n}\n\nexport async function fetchManual(\n fetchImpl: ManualFetch,\n url: string,\n init: ManualInit,\n wrapNetwork: (err: unknown) => Error,\n): Promise<Awaited<ReturnType<ManualFetch>>> {\n try {\n return await sendManual(fetchImpl, url, init);\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n throw error;\n }\n throw wrapNetwork(error);\n }\n}\n"],"mappings":";AAgCA,SAAgB,mBAAmB,SAAmC;CACpE,IAAI,OAAO,WAAW,UAAU,YAC9B,MAAM,QAAQ;CAEhB,MAAM,YAAY,WAAW,MAAM,KAAK,UAAU;CAGlD,OAAO,OAAO,KAAK,SACjB,UAAU,KAAK,SAAS,KAAA,IAAY,OAAO;EAAE,GAAG;EAAM,QAAQ,KAAK,UAAU;CAAK,CAAC;AACvF;;AAGA,eAAsB,WACpB,WACA,KACA,MAC2C;CAC3C,OAAO,UAAU,KAAK;EAAE,GAAG;EAAM,UAAU;CAAS,CAAC;AACvD;AAEA,eAAsB,YACpB,WACA,KACA,MACA,aAC2C;CAC3C,IAAI;EACF,OAAO,MAAM,WAAW,WAAW,KAAK,IAAI;CAC9C,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAS,MAAM,SAAS,cAC3C,MAAM;EAER,MAAM,YAAY,KAAK;CACzB;AACF"}
1
+ {"version":3,"file":"http-CuazNRIP.mjs","names":[],"sources":["../src/nips/http.ts"],"sourcesContent":["/**\n * Shared nips HTTP primitive. Not a pack entry (core has zero network). Callers never pass\n * `redirect`; sendManual always sets `\"manual\"`.\n */\n\nexport type ManualFetchInit = {\n method?: string;\n headers?: Record<string, string>;\n // oxlint-disable-next-line typescript/no-restricted-types -- mirrors RequestInit.body, which is nullable\n body?: Blob | FormData | string | null;\n signal?: AbortSignal | undefined;\n /**\n * Always `\"manual\"`: NIP-05, NIP-11 and Blossom responses must never be followed through\n * redirects. Adapters must forward this to their underlying fetch implementation.\n */\n redirect: \"manual\";\n};\n\nexport type ManualFetch = (\n url: string,\n init?: ManualFetchInit,\n) => Promise<{\n ok: boolean;\n status: number;\n // oxlint-disable-next-line typescript/no-restricted-types -- mirrors Headers.get, which returns null\n headers: { get: (name: string) => string | null };\n json: () => Promise<unknown>;\n arrayBuffer: () => Promise<ArrayBuffer>;\n}>;\n\ntype ManualInit = Omit<ManualFetchInit, \"redirect\">;\n\nexport function requireGlobalFetch(missing: () => Error): ManualFetch {\n if (typeof globalThis.fetch !== \"function\") {\n throw missing();\n }\n const fetchImpl = globalThis.fetch.bind(globalThis);\n // RequestInit.signal is AbortSignal | null and rejects explicit undefined under\n // exactOptionalPropertyTypes; normalize it for the real fetch.\n return async (url, init) =>\n fetchImpl(url, init === undefined ? init : { ...init, signal: init.signal ?? null });\n}\n\n/** Always sets redirect:manual. Shared by fetchManual and headStatus. */\nexport async function sendManual(\n fetchImpl: ManualFetch,\n url: string,\n init: ManualInit,\n): Promise<Awaited<ReturnType<ManualFetch>>> {\n return fetchImpl(url, { ...init, redirect: \"manual\" });\n}\n\nexport async function fetchManual(\n fetchImpl: ManualFetch,\n url: string,\n init: ManualInit,\n wrapNetwork: (err: unknown) => Error,\n): Promise<Awaited<ReturnType<ManualFetch>>> {\n try {\n return await sendManual(fetchImpl, url, init);\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n throw error;\n }\n throw wrapNetwork(error);\n }\n}\n"],"mappings":";AAgCA,SAAgB,mBAAmB,SAAmC;CACpE,IAAI,OAAO,WAAW,UAAU,YAC9B,MAAM,QAAQ;CAEhB,MAAM,YAAY,WAAW,MAAM,KAAK,UAAU;CAGlD,OAAO,OAAO,KAAK,SACjB,UAAU,KAAK,SAAS,KAAA,IAAY,OAAO;EAAE,GAAG;EAAM,QAAQ,KAAK,UAAU;CAAK,CAAC;AACvF;;AAGA,eAAsB,WACpB,WACA,KACA,MAC2C;CAC3C,OAAO,UAAU,KAAK;EAAE,GAAG;EAAM,UAAU;CAAS,CAAC;AACvD;AAEA,eAAsB,YACpB,WACA,KACA,MACA,aAC2C;CAC3C,IAAI;EACF,OAAO,MAAM,WAAW,WAAW,KAAK,IAAI;CAC9C,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAS,MAAM,SAAS,cAC3C,MAAM;EAER,MAAM,YAAY,KAAK;CACzB;AACF"}
@@ -1,7 +1,7 @@
1
1
  import { t as Event } from "./event-j3BwI5dF.mjs";
2
- import { t as Filter } from "./filter-PG7r79rt.mjs";
3
- import { a as NostrError } from "./error-BquXDmIN.mjs";
4
- import { _ as Closer, n as Pool } from "./pool-DUuYJ0x4.mjs";
2
+ import { t as Filter } from "./filter-oYG2zjJ3.mjs";
3
+ import { a as NostrError } from "./error-BPqAU5RB.mjs";
4
+ import { _ as Closer, n as Pool } from "./pool-MehgIIzm.mjs";
5
5
  //#region src/relay/error.d.ts
6
6
  /** Base class for relay errors; carries the relay URL when known. */
7
7
  declare class RelayError extends NostrError {
@@ -52,15 +52,15 @@ type RoutedJob = {
52
52
  };
53
53
  type FanInOptions = {
54
54
  /** First receipt only (deduped across jobs). `relayUrl` is the normalized relay URL. */
55
- onevent?: ((event: Event, relayUrl: string) => void) | undefined;
56
- oneose?: (() => void) | undefined;
57
- onclose?: ((reason: string) => void) | undefined;
55
+ onEvent?: ((event: Event, relayUrl: string) => void) | undefined;
56
+ onEose?: (() => void) | undefined;
57
+ onClose?: ((reason: string) => void) | undefined;
58
58
  signal?: AbortSignal | undefined;
59
59
  /** Armed at fan-in only. Never forwarded to relay.subscribe. */
60
60
  eoseTimeoutMs?: number | undefined;
61
61
  alreadyHaveEvent?: ((id: string) => boolean) | undefined;
62
62
  /** Every receipt from every relay, including skipped duplicates. */
63
- receivedEvent?: ((id: string, relayUrl: string) => void) | undefined;
63
+ onEventSeen?: ((id: string, relayUrl: string) => void) | undefined;
64
64
  closeOnEose?: boolean | undefined;
65
65
  /** Optional. ensureRelay uses Pool.#opts.connectTimeoutMs when omitted. */
66
66
  connectTimeoutMs?: number | undefined;
@@ -75,8 +75,8 @@ declare function fetchRouted(pool: Pool, jobs: ReadonlyArray<RoutedJob>, opts?:
75
75
  signal?: AbortSignal | undefined;
76
76
  connectTimeoutMs?: number | undefined;
77
77
  /** Every event of every relay batch, including cross-relay duplicates. */
78
- onevent?: ((event: Event, relayUrl: string) => void) | undefined;
78
+ onEvent?: ((event: Event, relayUrl: string) => void) | undefined;
79
79
  }): Promise<Event[]>;
80
80
  //#endregion
81
81
  export { isInsecureRelayUrl as a, RelayError as c, RelayTimeoutError as d, fetchRouted as i, RelayPublishError as l, RoutedJob as n, RelayClosedError as o, fanIn as r, RelayConnectionError as s, FanInOptions as t, RelaySuspendedError as u };
82
- //# sourceMappingURL=index-DD0Ywpay.d.mts.map
82
+ //# sourceMappingURL=index-B2JlP2Jf.d.mts.map
@@ -1,13 +1,13 @@
1
1
  import { t as Event } from "./event-j3BwI5dF.mjs";
2
- import { n as ProfileMetadata } from "./builder-BrmLOVj_.mjs";
3
- import { t as RelayListItem } from "./nip65-DaWMg943.mjs";
4
- import { t as Gossip } from "./gossip-u0iBlZ1U.mjs";
5
- import { a as NostrError } from "./error-BquXDmIN.mjs";
6
- import { _ as Closer, n as Pool } from "./pool-DUuYJ0x4.mjs";
7
- import { r as OutboxBound, t as EventStore } from "./types-CqhnT-Mx.mjs";
8
- import { t as ReactiveEventStore } from "./reactive-jyKrA9ej.mjs";
9
- import { i as EventPointer, t as AddressPointer } from "./nip19-DpPN17bY.mjs";
10
- import { t as MuteItem } from "./nip51-D_bu8Hph.mjs";
2
+ import { n as ProfileMetadata } from "./builder-C72nJITG.mjs";
3
+ import { t as RelayListItem } from "./nip65-CPNE0SV0.mjs";
4
+ import { t as Gossip } from "./gossip-BvCItMVZ.mjs";
5
+ import { a as NostrError } from "./error-BPqAU5RB.mjs";
6
+ import { _ as Closer, n as Pool } from "./pool-MehgIIzm.mjs";
7
+ import { r as OutboxBound, t as EventStore } from "./types-DiVVyf1e.mjs";
8
+ import { t as ReactiveEventStore } from "./reactive-B7lPFgTS.mjs";
9
+ import { i as EventPointer, t as AddressPointer } from "./nip19-D4RKn6GC.mjs";
10
+ import { t as MuteItem } from "./nip51-C_kTYcud.mjs";
11
11
  //#region src/loaders/event.d.ts
12
12
  type EventRef = string | EventPointer | AddressPointer;
13
13
  /**
@@ -186,4 +186,4 @@ type CreateLoadersOptions = {
186
186
  declare function createLoaders(opts: CreateLoadersOptions): Loaders;
187
187
  //#endregion
188
188
  export { EventRef as _, OutboxFeed as a, NostrUser as c, ListLoaders as d, ListResult as f, EventLoader as g, ReplaceableLoader as h, OutboxError as i, ProfileLoader as l, ReplaceableLoadResult as m, Loaders as n, OutboxFeedOptions as o, LoadStyle as p, createLoaders as r, groupAuthorsByOutboxRelay as s, CreateLoadersOptions as t, bareNostrUser as u };
189
- //# sourceMappingURL=index-yq9xkONo.d.mts.map
189
+ //# sourceMappingURL=index-DCobP2Jj.d.mts.map
@@ -13,6 +13,8 @@ declare const SUBSCRIPTION_ID_MAX_CHARS = 64;
13
13
  //#region src/core/util.d.ts
14
14
  /** Lowercase hex encode. */
15
15
  declare function bytesToHex(bytes: Uint8Array): string;
16
+ /** Cryptographically secure random bytes (Web Crypto under the hood). */
17
+ declare function randomBytes(length: number): Uint8Array;
16
18
  /** Decode lowercase or mixed-case hex to bytes. */
17
19
  declare function hexToBytes(hex: string): Uint8Array;
18
20
  /** True when value is canonical NIP-01 lowercase hex of 32 bytes (64 chars). */
@@ -37,8 +39,9 @@ declare function normalizeRelayUrls(urls: Iterable<string>): string[];
37
39
  //#endregion
38
40
  //#region src/core/kind.d.ts
39
41
  /**
40
- * NIP-01 regular ranges verbatim; kinds outside every defined range are still stored like regular
41
- * events (see {@link classifyKind}).
42
+ * Regular means relays store the event: every kind below 10000 except the replaceable kinds 0 and 3.
43
+ * The NIP-01 enumerated regular ranges are a subset; kinds 45–999 are stored like regular events in
44
+ * practice (nostr-tools and rust-nostr agree).
42
45
  */
43
46
  declare function isRegularKind(kind: number): boolean;
44
47
  /** Replaceable: latest per (pubkey, kind) wins. */
@@ -52,7 +55,7 @@ type KindClassification = "regular" | "replaceable" | "ephemeral" | "addressable
52
55
  /**
53
56
  * Classify a kind number into its NIP-01 storage class. NIP-01 leaves kinds 45–999 and 40000+
54
57
  * undefined (rather than forbidden); relays store them like regular events, so they classify
55
- * `"regular"` here even though {@link isRegularKind} returns false for them.
58
+ * `"regular"` here even though {@link isRegularKind} returns false for kinds 40000+.
56
59
  */
57
60
  declare function classifyKind(kind: number): KindClassification;
58
61
  /** Kind numbers used by this package. */
@@ -91,5 +94,5 @@ type KindName = keyof typeof Kind;
91
94
  /** Union of the numeric kind values on {@link Kind}. */
92
95
  type KnownKind = (typeof Kind)[KindName];
93
96
  //#endregion
94
- export { SUBSCRIPTION_ID_MAX_CHARS as S, normalizeURL as _, classifyKind as a, SECRET_KEY_BYTES as b, isRegularKind as c, assertSecretKeyBytes as d, bytesToHex as f, normalizeRelayUrls as g, isHex64 as h, KnownKind as i, isReplaceableKind as l, isHex32 as m, KindClassification as n, isAddressableKind as o, hexToBytes as p, KindName as r, isEphemeralKind as s, Kind as t, assertHex32 as u, EVENT_ID_BYTES as v, SIGNATURE_BYTES as x, PUBLIC_KEY_BYTES as y };
95
- //# sourceMappingURL=index-Hp26bgYG.d.mts.map
97
+ export { SUBSCRIPTION_ID_MAX_CHARS as C, SIGNATURE_BYTES as S, normalizeURL as _, classifyKind as a, PUBLIC_KEY_BYTES as b, isRegularKind as c, assertSecretKeyBytes as d, bytesToHex as f, normalizeRelayUrls as g, isHex64 as h, KnownKind as i, isReplaceableKind as l, isHex32 as m, KindClassification as n, isAddressableKind as o, hexToBytes as p, KindName as r, isEphemeralKind as s, Kind as t, assertHex32 as u, randomBytes as v, SECRET_KEY_BYTES as x, EVENT_ID_BYTES as y };
98
+ //# sourceMappingURL=index-Dpj_MUxb.d.mts.map
@@ -1,6 +1,6 @@
1
1
  import { t as Event } from "./event-j3BwI5dF.mjs";
2
- import { t as Filter } from "./filter-PG7r79rt.mjs";
3
- import { i as PutResult, n as NegentropyItem, r as OutboxBound, t as EventStore } from "./types-CqhnT-Mx.mjs";
2
+ import { t as Filter } from "./filter-oYG2zjJ3.mjs";
3
+ import { i as PutResult, n as NegentropyItem, r as OutboxBound, t as EventStore } from "./types-DiVVyf1e.mjs";
4
4
  //#region src/storage/memory-index.d.ts
5
5
  type MemoryIndexOptions = {
6
6
  /**
@@ -188,4 +188,4 @@ declare class SqliteEventStore implements EventStore {
188
188
  }
189
189
  //#endregion
190
190
  export { IndexedDbEventStoreOptions as a, MemoryIndexOptions as c, IndexedDbEventStore as i, SqlValue as n, MemoryEventStore as o, SqliteEventStore as r, MemoryIndex as s, SqlDriver as t };
191
- //# sourceMappingURL=index-rKcmI6ex.d.mts.map
191
+ //# sourceMappingURL=index-DzHmhHqD.d.mts.map
@@ -1,9 +1,9 @@
1
1
  import { n as EventTemplate, r as UnsignedEvent, t as Event } from "./event-j3BwI5dF.mjs";
2
- import { i as SecretKeyInput, t as Keys } from "./key-CwMVo14x.mjs";
3
- import { t as Filter } from "./filter-PG7r79rt.mjs";
4
- import { a as NostrError } from "./error-BquXDmIN.mjs";
2
+ import { i as SecretKeyInput, t as Keys } from "./key-DojW2QZl.mjs";
3
+ import { t as Filter } from "./filter-oYG2zjJ3.mjs";
4
+ import { a as NostrError } from "./error-BPqAU5RB.mjs";
5
5
  import { t as NostrSigner } from "./types-BJ4wZ7JJ.mjs";
6
- import { n as ClientMetadata, t as BunkerPointer } from "./nip46-BHaxC9TZ.mjs";
6
+ import { n as ClientMetadata, t as BunkerPointer } from "./nip46-i8L8_nIm.mjs";
7
7
  //#region src/signer/error.d.ts
8
8
  /**
9
9
  * Thrown by a lazy NIP-42 AUTH sign function when no signer is configured at challenge time.
@@ -119,8 +119,8 @@ declare class Nip07Signer implements NostrSigner {
119
119
  /** Subscribe options used by NIP-46 transport (structural subset of Pool). */
120
120
  type Nip46SubscribeOptions = {
121
121
  signal?: AbortSignal | undefined;
122
- onevent?: ((event: Event) => void) | undefined;
123
- onclose?: ((reason: string) => void) | undefined;
122
+ onEvent?: ((event: Event) => void) | undefined;
123
+ onClose?: ((reason: string) => void) | undefined;
124
124
  };
125
125
  /**
126
126
  * Structural relay transport for NIP-46 RPC. Satisfied by {@link import("../relay/pool.ts").Pool};
@@ -224,4 +224,4 @@ declare class Nip46Signer implements NostrSigner {
224
224
  }
225
225
  //#endregion
226
226
  export { Nip07Signer as a, isNip07Available as c, NostrKeyOperations as d, NoSignerError as f, Nip46Transport as i, KeysSigner as l, Nip46SignerOptions as n, WindowNostr as o, SignerDisposedError as p, Nip46SubscribeOptions as r, getWindowNostr as s, Nip46Signer as t, DelegatedSigner as u };
227
- //# sourceMappingURL=index-SwMknboJ.d.mts.map
227
+ //# sourceMappingURL=index-YID-zPDF.d.mts.map