@bsv/sdk 2.1.0 → 2.1.2

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 (392) hide show
  1. package/README.md +7 -7
  2. package/dist/cjs/package.json +1 -1
  3. package/dist/cjs/src/auth/Peer.js +8 -13
  4. package/dist/cjs/src/auth/Peer.js.map +1 -1
  5. package/dist/cjs/src/auth/SessionManager.js +4 -7
  6. package/dist/cjs/src/auth/SessionManager.js.map +1 -1
  7. package/dist/cjs/src/auth/certificates/MasterCertificate.js +1 -1
  8. package/dist/cjs/src/auth/certificates/MasterCertificate.js.map +1 -1
  9. package/dist/cjs/src/auth/certificates/__tests/CompletedProtoWallet.js +1 -1
  10. package/dist/cjs/src/auth/certificates/__tests/CompletedProtoWallet.js.map +1 -1
  11. package/dist/cjs/src/auth/clients/AuthFetch.js +32 -32
  12. package/dist/cjs/src/auth/clients/AuthFetch.js.map +1 -1
  13. package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js +4 -4
  14. package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
  15. package/dist/cjs/src/compat/ECIES.js +29 -34
  16. package/dist/cjs/src/compat/ECIES.js.map +1 -1
  17. package/dist/cjs/src/compat/HD.js +9 -4
  18. package/dist/cjs/src/compat/HD.js.map +1 -1
  19. package/dist/cjs/src/compat/Mnemonic.js +12 -12
  20. package/dist/cjs/src/compat/Mnemonic.js.map +1 -1
  21. package/dist/cjs/src/identity/ContactsManager.js +212 -234
  22. package/dist/cjs/src/identity/ContactsManager.js.map +1 -1
  23. package/dist/cjs/src/identity/IdentityClient.js +199 -63
  24. package/dist/cjs/src/identity/IdentityClient.js.map +1 -1
  25. package/dist/cjs/src/kvstore/GlobalKVStore.js +30 -31
  26. package/dist/cjs/src/kvstore/GlobalKVStore.js.map +1 -1
  27. package/dist/cjs/src/kvstore/LocalKVStore.js +9 -9
  28. package/dist/cjs/src/kvstore/LocalKVStore.js.map +1 -1
  29. package/dist/cjs/src/kvstore/kvStoreInterpreter.js +2 -2
  30. package/dist/cjs/src/kvstore/kvStoreInterpreter.js.map +1 -1
  31. package/dist/cjs/src/messages/SignedMessage.js +1 -1
  32. package/dist/cjs/src/messages/SignedMessage.js.map +1 -1
  33. package/dist/cjs/src/overlay-tools/Historian.js +1 -1
  34. package/dist/cjs/src/overlay-tools/Historian.js.map +1 -1
  35. package/dist/cjs/src/overlay-tools/LookupResolver.js +213 -93
  36. package/dist/cjs/src/overlay-tools/LookupResolver.js.map +1 -1
  37. package/dist/cjs/src/overlay-tools/SHIPBroadcaster.js +75 -146
  38. package/dist/cjs/src/overlay-tools/SHIPBroadcaster.js.map +1 -1
  39. package/dist/cjs/src/primitives/AESGCM.js +2 -2
  40. package/dist/cjs/src/primitives/AESGCM.js.map +1 -1
  41. package/dist/cjs/src/primitives/BigNumber.js +164 -148
  42. package/dist/cjs/src/primitives/BigNumber.js.map +1 -1
  43. package/dist/cjs/src/primitives/Curve.js +17 -15
  44. package/dist/cjs/src/primitives/Curve.js.map +1 -1
  45. package/dist/cjs/src/primitives/ECDSA.js +12 -7
  46. package/dist/cjs/src/primitives/ECDSA.js.map +1 -1
  47. package/dist/cjs/src/primitives/Hash.js +312 -105
  48. package/dist/cjs/src/primitives/Hash.js.map +1 -1
  49. package/dist/cjs/src/primitives/JacobianPoint.js +8 -8
  50. package/dist/cjs/src/primitives/JacobianPoint.js.map +1 -1
  51. package/dist/cjs/src/primitives/K256.js +3 -3
  52. package/dist/cjs/src/primitives/K256.js.map +1 -1
  53. package/dist/cjs/src/primitives/Point.js +36 -40
  54. package/dist/cjs/src/primitives/Point.js.map +1 -1
  55. package/dist/cjs/src/primitives/PrivateKey.js +4 -4
  56. package/dist/cjs/src/primitives/PrivateKey.js.map +1 -1
  57. package/dist/cjs/src/primitives/PublicKey.js +4 -4
  58. package/dist/cjs/src/primitives/PublicKey.js.map +1 -1
  59. package/dist/cjs/src/primitives/Random.js +10 -14
  60. package/dist/cjs/src/primitives/Random.js.map +1 -1
  61. package/dist/cjs/src/primitives/ReaderUint8Array.js +6 -6
  62. package/dist/cjs/src/primitives/ReaderUint8Array.js.map +1 -1
  63. package/dist/cjs/src/primitives/Schnorr.js +2 -2
  64. package/dist/cjs/src/primitives/Schnorr.js.map +1 -1
  65. package/dist/cjs/src/primitives/Secp256r1.js +2 -1
  66. package/dist/cjs/src/primitives/Secp256r1.js.map +1 -1
  67. package/dist/cjs/src/primitives/Signature.js +8 -8
  68. package/dist/cjs/src/primitives/Signature.js.map +1 -1
  69. package/dist/cjs/src/primitives/SymmetricKey.js +123 -1
  70. package/dist/cjs/src/primitives/SymmetricKey.js.map +1 -1
  71. package/dist/cjs/src/primitives/TransactionSignature.js +20 -21
  72. package/dist/cjs/src/primitives/TransactionSignature.js.map +1 -1
  73. package/dist/cjs/src/primitives/utils.js +39 -46
  74. package/dist/cjs/src/primitives/utils.js.map +1 -1
  75. package/dist/cjs/src/registry/RegistryClient.js +31 -23
  76. package/dist/cjs/src/registry/RegistryClient.js.map +1 -1
  77. package/dist/cjs/src/remittance/RemittanceManager.js +19 -18
  78. package/dist/cjs/src/remittance/RemittanceManager.js.map +1 -1
  79. package/dist/cjs/src/remittance/modules/BasicBRC29.js.map +1 -1
  80. package/dist/cjs/src/script/Script.js +93 -170
  81. package/dist/cjs/src/script/Script.js.map +1 -1
  82. package/dist/cjs/src/script/ScriptEvaluationError.js +2 -2
  83. package/dist/cjs/src/script/ScriptEvaluationError.js.map +1 -1
  84. package/dist/cjs/src/script/Spend.js +14 -12
  85. package/dist/cjs/src/script/Spend.js.map +1 -1
  86. package/dist/cjs/src/script/templates/PushDrop.js +22 -18
  87. package/dist/cjs/src/script/templates/PushDrop.js.map +1 -1
  88. package/dist/cjs/src/script/templates/RPuzzle.js +2 -4
  89. package/dist/cjs/src/script/templates/RPuzzle.js.map +1 -1
  90. package/dist/cjs/src/storage/StorageDownloader.js +42 -9
  91. package/dist/cjs/src/storage/StorageDownloader.js.map +1 -1
  92. package/dist/cjs/src/totp/totp.js +1 -1
  93. package/dist/cjs/src/totp/totp.js.map +1 -1
  94. package/dist/cjs/src/transaction/Beef.js +239 -192
  95. package/dist/cjs/src/transaction/Beef.js.map +1 -1
  96. package/dist/cjs/src/transaction/BeefConstants.js +19 -0
  97. package/dist/cjs/src/transaction/BeefConstants.js.map +1 -0
  98. package/dist/cjs/src/transaction/BeefTx.js +12 -12
  99. package/dist/cjs/src/transaction/BeefTx.js.map +1 -1
  100. package/dist/cjs/src/transaction/MerklePath.js +4 -4
  101. package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
  102. package/dist/cjs/src/transaction/Transaction.js +49 -52
  103. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  104. package/dist/cjs/src/transaction/fee-models/SatoshisPerKilobyte.js +1 -1
  105. package/dist/cjs/src/transaction/fee-models/SatoshisPerKilobyte.js.map +1 -1
  106. package/dist/cjs/src/transaction/http/BinaryFetchClient.js +9 -9
  107. package/dist/cjs/src/transaction/http/BinaryFetchClient.js.map +1 -1
  108. package/dist/cjs/src/transaction/http/DefaultHttpClient.js +9 -9
  109. package/dist/cjs/src/transaction/http/DefaultHttpClient.js.map +1 -1
  110. package/dist/cjs/src/wallet/CachedKeyDeriver.js +1 -1
  111. package/dist/cjs/src/wallet/CachedKeyDeriver.js.map +1 -1
  112. package/dist/cjs/src/wallet/WalletClient.js.map +1 -1
  113. package/dist/cjs/src/wallet/WalletError.js.map +1 -1
  114. package/dist/cjs/src/wallet/substrates/HTTPWalletJSON.js +5 -4
  115. package/dist/cjs/src/wallet/substrates/HTTPWalletJSON.js.map +1 -1
  116. package/dist/cjs/src/wallet/substrates/ReactNativeWebView.js +9 -9
  117. package/dist/cjs/src/wallet/substrates/ReactNativeWebView.js.map +1 -1
  118. package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js +92 -92
  119. package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js.map +1 -1
  120. package/dist/cjs/src/wallet/substrates/WalletWireTransceiver.js +387 -711
  121. package/dist/cjs/src/wallet/substrates/WalletWireTransceiver.js.map +1 -1
  122. package/dist/cjs/src/wallet/substrates/XDM.js +4 -4
  123. package/dist/cjs/src/wallet/substrates/XDM.js.map +1 -1
  124. package/dist/cjs/src/wallet/substrates/window.CWI.js +2 -2
  125. package/dist/cjs/src/wallet/substrates/window.CWI.js.map +1 -1
  126. package/dist/cjs/src/wallet/validationHelpers.js +9 -9
  127. package/dist/cjs/src/wallet/validationHelpers.js.map +1 -1
  128. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  129. package/dist/esm/src/auth/Peer.js +25 -13
  130. package/dist/esm/src/auth/Peer.js.map +1 -1
  131. package/dist/esm/src/auth/SessionManager.js +4 -7
  132. package/dist/esm/src/auth/SessionManager.js.map +1 -1
  133. package/dist/esm/src/auth/certificates/MasterCertificate.js +1 -1
  134. package/dist/esm/src/auth/certificates/MasterCertificate.js.map +1 -1
  135. package/dist/esm/src/auth/certificates/__tests/CompletedProtoWallet.js +1 -1
  136. package/dist/esm/src/auth/certificates/__tests/CompletedProtoWallet.js.map +1 -1
  137. package/dist/esm/src/auth/clients/AuthFetch.js +32 -32
  138. package/dist/esm/src/auth/clients/AuthFetch.js.map +1 -1
  139. package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js +4 -4
  140. package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
  141. package/dist/esm/src/compat/ECIES.js +29 -34
  142. package/dist/esm/src/compat/ECIES.js.map +1 -1
  143. package/dist/esm/src/compat/HD.js +9 -4
  144. package/dist/esm/src/compat/HD.js.map +1 -1
  145. package/dist/esm/src/compat/Mnemonic.js +12 -12
  146. package/dist/esm/src/compat/Mnemonic.js.map +1 -1
  147. package/dist/esm/src/identity/ContactsManager.js +212 -234
  148. package/dist/esm/src/identity/ContactsManager.js.map +1 -1
  149. package/dist/esm/src/identity/IdentityClient.js +199 -63
  150. package/dist/esm/src/identity/IdentityClient.js.map +1 -1
  151. package/dist/esm/src/kvstore/GlobalKVStore.js +30 -31
  152. package/dist/esm/src/kvstore/GlobalKVStore.js.map +1 -1
  153. package/dist/esm/src/kvstore/LocalKVStore.js +9 -9
  154. package/dist/esm/src/kvstore/LocalKVStore.js.map +1 -1
  155. package/dist/esm/src/kvstore/kvStoreInterpreter.js +2 -2
  156. package/dist/esm/src/kvstore/kvStoreInterpreter.js.map +1 -1
  157. package/dist/esm/src/messages/SignedMessage.js +1 -1
  158. package/dist/esm/src/messages/SignedMessage.js.map +1 -1
  159. package/dist/esm/src/overlay-tools/Historian.js +1 -1
  160. package/dist/esm/src/overlay-tools/Historian.js.map +1 -1
  161. package/dist/esm/src/overlay-tools/LookupResolver.js +213 -93
  162. package/dist/esm/src/overlay-tools/LookupResolver.js.map +1 -1
  163. package/dist/esm/src/overlay-tools/SHIPBroadcaster.js +74 -146
  164. package/dist/esm/src/overlay-tools/SHIPBroadcaster.js.map +1 -1
  165. package/dist/esm/src/primitives/AESGCM.js +2 -2
  166. package/dist/esm/src/primitives/AESGCM.js.map +1 -1
  167. package/dist/esm/src/primitives/BigNumber.js +167 -154
  168. package/dist/esm/src/primitives/BigNumber.js.map +1 -1
  169. package/dist/esm/src/primitives/Curve.js +17 -15
  170. package/dist/esm/src/primitives/Curve.js.map +1 -1
  171. package/dist/esm/src/primitives/ECDSA.js +12 -7
  172. package/dist/esm/src/primitives/ECDSA.js.map +1 -1
  173. package/dist/esm/src/primitives/Hash.js +316 -105
  174. package/dist/esm/src/primitives/Hash.js.map +1 -1
  175. package/dist/esm/src/primitives/JacobianPoint.js +8 -8
  176. package/dist/esm/src/primitives/JacobianPoint.js.map +1 -1
  177. package/dist/esm/src/primitives/K256.js +3 -3
  178. package/dist/esm/src/primitives/K256.js.map +1 -1
  179. package/dist/esm/src/primitives/Point.js +36 -40
  180. package/dist/esm/src/primitives/Point.js.map +1 -1
  181. package/dist/esm/src/primitives/PrivateKey.js +4 -4
  182. package/dist/esm/src/primitives/PrivateKey.js.map +1 -1
  183. package/dist/esm/src/primitives/PublicKey.js +4 -4
  184. package/dist/esm/src/primitives/PublicKey.js.map +1 -1
  185. package/dist/esm/src/primitives/Random.js +10 -14
  186. package/dist/esm/src/primitives/Random.js.map +1 -1
  187. package/dist/esm/src/primitives/ReaderUint8Array.js +6 -6
  188. package/dist/esm/src/primitives/ReaderUint8Array.js.map +1 -1
  189. package/dist/esm/src/primitives/Schnorr.js +1 -1
  190. package/dist/esm/src/primitives/Schnorr.js.map +1 -1
  191. package/dist/esm/src/primitives/Secp256r1.js +2 -1
  192. package/dist/esm/src/primitives/Secp256r1.js.map +1 -1
  193. package/dist/esm/src/primitives/Signature.js +8 -8
  194. package/dist/esm/src/primitives/Signature.js.map +1 -1
  195. package/dist/esm/src/primitives/SymmetricKey.js +123 -1
  196. package/dist/esm/src/primitives/SymmetricKey.js.map +1 -1
  197. package/dist/esm/src/primitives/TransactionSignature.js +20 -21
  198. package/dist/esm/src/primitives/TransactionSignature.js.map +1 -1
  199. package/dist/esm/src/primitives/utils.js +39 -48
  200. package/dist/esm/src/primitives/utils.js.map +1 -1
  201. package/dist/esm/src/registry/RegistryClient.js +31 -23
  202. package/dist/esm/src/registry/RegistryClient.js.map +1 -1
  203. package/dist/esm/src/remittance/RemittanceManager.js +19 -18
  204. package/dist/esm/src/remittance/RemittanceManager.js.map +1 -1
  205. package/dist/esm/src/remittance/modules/BasicBRC29.js.map +1 -1
  206. package/dist/esm/src/script/Script.js +93 -170
  207. package/dist/esm/src/script/Script.js.map +1 -1
  208. package/dist/esm/src/script/ScriptEvaluationError.js +2 -2
  209. package/dist/esm/src/script/ScriptEvaluationError.js.map +1 -1
  210. package/dist/esm/src/script/Spend.js +14 -12
  211. package/dist/esm/src/script/Spend.js.map +1 -1
  212. package/dist/esm/src/script/templates/PushDrop.js +4 -3
  213. package/dist/esm/src/script/templates/PushDrop.js.map +1 -1
  214. package/dist/esm/src/script/templates/RPuzzle.js +2 -4
  215. package/dist/esm/src/script/templates/RPuzzle.js.map +1 -1
  216. package/dist/esm/src/storage/StorageDownloader.js +1 -1
  217. package/dist/esm/src/storage/StorageDownloader.js.map +1 -1
  218. package/dist/esm/src/totp/totp.js +1 -1
  219. package/dist/esm/src/totp/totp.js.map +1 -1
  220. package/dist/esm/src/transaction/Beef.js +229 -186
  221. package/dist/esm/src/transaction/Beef.js.map +1 -1
  222. package/dist/esm/src/transaction/BeefConstants.js +16 -0
  223. package/dist/esm/src/transaction/BeefConstants.js.map +1 -0
  224. package/dist/esm/src/transaction/BeefTx.js +3 -3
  225. package/dist/esm/src/transaction/BeefTx.js.map +1 -1
  226. package/dist/esm/src/transaction/MerklePath.js +4 -4
  227. package/dist/esm/src/transaction/MerklePath.js.map +1 -1
  228. package/dist/esm/src/transaction/Transaction.js +49 -52
  229. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  230. package/dist/esm/src/transaction/fee-models/SatoshisPerKilobyte.js +1 -1
  231. package/dist/esm/src/transaction/fee-models/SatoshisPerKilobyte.js.map +1 -1
  232. package/dist/esm/src/transaction/http/BinaryFetchClient.js +9 -9
  233. package/dist/esm/src/transaction/http/BinaryFetchClient.js.map +1 -1
  234. package/dist/esm/src/transaction/http/DefaultHttpClient.js +9 -9
  235. package/dist/esm/src/transaction/http/DefaultHttpClient.js.map +1 -1
  236. package/dist/esm/src/wallet/CachedKeyDeriver.js +1 -1
  237. package/dist/esm/src/wallet/CachedKeyDeriver.js.map +1 -1
  238. package/dist/esm/src/wallet/WalletClient.js.map +1 -1
  239. package/dist/esm/src/wallet/WalletError.js.map +1 -1
  240. package/dist/esm/src/wallet/substrates/HTTPWalletJSON.js +5 -4
  241. package/dist/esm/src/wallet/substrates/HTTPWalletJSON.js.map +1 -1
  242. package/dist/esm/src/wallet/substrates/ReactNativeWebView.js +9 -9
  243. package/dist/esm/src/wallet/substrates/ReactNativeWebView.js.map +1 -1
  244. package/dist/esm/src/wallet/substrates/WalletWireProcessor.js +92 -92
  245. package/dist/esm/src/wallet/substrates/WalletWireProcessor.js.map +1 -1
  246. package/dist/esm/src/wallet/substrates/WalletWireTransceiver.js +387 -711
  247. package/dist/esm/src/wallet/substrates/WalletWireTransceiver.js.map +1 -1
  248. package/dist/esm/src/wallet/substrates/XDM.js +4 -4
  249. package/dist/esm/src/wallet/substrates/XDM.js.map +1 -1
  250. package/dist/esm/src/wallet/substrates/window.CWI.js +2 -2
  251. package/dist/esm/src/wallet/substrates/window.CWI.js.map +1 -1
  252. package/dist/esm/src/wallet/validationHelpers.js +9 -9
  253. package/dist/esm/src/wallet/validationHelpers.js.map +1 -1
  254. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  255. package/dist/types/src/auth/Peer.d.ts +13 -0
  256. package/dist/types/src/auth/Peer.d.ts.map +1 -1
  257. package/dist/types/src/auth/SessionManager.d.ts.map +1 -1
  258. package/dist/types/src/auth/clients/AuthFetch.d.ts.map +1 -1
  259. package/dist/types/src/compat/ECIES.d.ts.map +1 -1
  260. package/dist/types/src/compat/HD.d.ts.map +1 -1
  261. package/dist/types/src/identity/ContactsManager.d.ts +31 -2
  262. package/dist/types/src/identity/ContactsManager.d.ts.map +1 -1
  263. package/dist/types/src/identity/IdentityClient.d.ts +75 -10
  264. package/dist/types/src/identity/IdentityClient.d.ts.map +1 -1
  265. package/dist/types/src/kvstore/GlobalKVStore.d.ts.map +1 -1
  266. package/dist/types/src/overlay-tools/LookupResolver.d.ts +73 -2
  267. package/dist/types/src/overlay-tools/LookupResolver.d.ts.map +1 -1
  268. package/dist/types/src/overlay-tools/SHIPBroadcaster.d.ts +18 -3
  269. package/dist/types/src/overlay-tools/SHIPBroadcaster.d.ts.map +1 -1
  270. package/dist/types/src/primitives/BigNumber.d.ts +13 -3
  271. package/dist/types/src/primitives/BigNumber.d.ts.map +1 -1
  272. package/dist/types/src/primitives/Curve.d.ts.map +1 -1
  273. package/dist/types/src/primitives/ECDSA.d.ts.map +1 -1
  274. package/dist/types/src/primitives/Hash.d.ts +22 -17
  275. package/dist/types/src/primitives/Hash.d.ts.map +1 -1
  276. package/dist/types/src/primitives/JacobianPoint.d.ts +3 -1
  277. package/dist/types/src/primitives/JacobianPoint.d.ts.map +1 -1
  278. package/dist/types/src/primitives/Point.d.ts.map +1 -1
  279. package/dist/types/src/primitives/Random.d.ts +2 -2
  280. package/dist/types/src/primitives/Random.d.ts.map +1 -1
  281. package/dist/types/src/primitives/ReaderUint8Array.d.ts.map +1 -1
  282. package/dist/types/src/primitives/Schnorr.d.ts +2 -1
  283. package/dist/types/src/primitives/Schnorr.d.ts.map +1 -1
  284. package/dist/types/src/primitives/Secp256r1.d.ts.map +1 -1
  285. package/dist/types/src/primitives/SymmetricKey.d.ts.map +1 -1
  286. package/dist/types/src/primitives/utils.d.ts +2 -4
  287. package/dist/types/src/primitives/utils.d.ts.map +1 -1
  288. package/dist/types/src/registry/RegistryClient.d.ts.map +1 -1
  289. package/dist/types/src/remittance/RemittanceManager.d.ts.map +1 -1
  290. package/dist/types/src/remittance/modules/BasicBRC29.d.ts.map +1 -1
  291. package/dist/types/src/script/Script.d.ts +15 -8
  292. package/dist/types/src/script/Script.d.ts.map +1 -1
  293. package/dist/types/src/script/Spend.d.ts.map +1 -1
  294. package/dist/types/src/script/templates/PushDrop.d.ts +3 -1
  295. package/dist/types/src/script/templates/PushDrop.d.ts.map +1 -1
  296. package/dist/types/src/script/templates/RPuzzle.d.ts.map +1 -1
  297. package/dist/types/src/transaction/Beef.d.ts +46 -8
  298. package/dist/types/src/transaction/Beef.d.ts.map +1 -1
  299. package/dist/types/src/transaction/BeefConstants.d.ts +15 -0
  300. package/dist/types/src/transaction/BeefConstants.d.ts.map +1 -0
  301. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  302. package/dist/types/src/wallet/CachedKeyDeriver.d.ts.map +1 -1
  303. package/dist/types/src/wallet/KeyDeriver.d.ts +1 -1
  304. package/dist/types/src/wallet/KeyDeriver.d.ts.map +1 -1
  305. package/dist/types/src/wallet/Wallet.interfaces.d.ts +18 -3
  306. package/dist/types/src/wallet/Wallet.interfaces.d.ts.map +1 -1
  307. package/dist/types/src/wallet/WalletClient.d.ts +8 -8
  308. package/dist/types/src/wallet/WalletClient.d.ts.map +1 -1
  309. package/dist/types/src/wallet/substrates/HTTPWalletJSON.d.ts +7 -7
  310. package/dist/types/src/wallet/substrates/HTTPWalletJSON.d.ts.map +1 -1
  311. package/dist/types/src/wallet/substrates/WalletWireTransceiver.d.ts +36 -7
  312. package/dist/types/src/wallet/substrates/WalletWireTransceiver.d.ts.map +1 -1
  313. package/dist/types/src/wallet/substrates/window.CWI.d.ts +9 -9
  314. package/dist/types/src/wallet/substrates/window.CWI.d.ts.map +1 -1
  315. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  316. package/dist/umd/bundle.js +3 -3
  317. package/package.json +1 -1
  318. package/src/auth/Peer.ts +26 -13
  319. package/src/auth/SessionManager.ts +4 -7
  320. package/src/auth/certificates/MasterCertificate.ts +1 -1
  321. package/src/auth/certificates/__tests/CompletedProtoWallet.ts +1 -1
  322. package/src/auth/clients/AuthFetch.ts +41 -41
  323. package/src/auth/transports/SimplifiedFetchTransport.ts +4 -4
  324. package/src/compat/ECIES.ts +29 -34
  325. package/src/compat/HD.ts +10 -5
  326. package/src/compat/Mnemonic.ts +11 -11
  327. package/src/compat/__tests/HD.test.ts +19 -0
  328. package/src/identity/ContactsManager.ts +236 -258
  329. package/src/identity/IdentityClient.ts +244 -71
  330. package/src/identity/__tests/IdentityClient.additional.test.ts +150 -1
  331. package/src/identity/__tests/IdentityClient.test.ts +27 -3
  332. package/src/kvstore/GlobalKVStore.ts +31 -32
  333. package/src/kvstore/LocalKVStore.ts +8 -8
  334. package/src/kvstore/kvStoreInterpreter.ts +2 -2
  335. package/src/messages/SignedMessage.ts +1 -1
  336. package/src/overlay-tools/Historian.ts +1 -1
  337. package/src/overlay-tools/LookupResolver.ts +264 -90
  338. package/src/overlay-tools/SHIPBroadcaster.ts +92 -168
  339. package/src/primitives/AESGCM.ts +2 -2
  340. package/src/primitives/BigNumber.ts +122 -113
  341. package/src/primitives/Curve.ts +16 -15
  342. package/src/primitives/ECDSA.ts +10 -8
  343. package/src/primitives/Hash.ts +381 -146
  344. package/src/primitives/JacobianPoint.ts +13 -11
  345. package/src/primitives/K256.ts +3 -3
  346. package/src/primitives/Point.ts +35 -38
  347. package/src/primitives/PrivateKey.ts +3 -3
  348. package/src/primitives/PublicKey.ts +3 -3
  349. package/src/primitives/Random.ts +11 -14
  350. package/src/primitives/ReaderUint8Array.ts +7 -7
  351. package/src/primitives/Schnorr.ts +2 -1
  352. package/src/primitives/Secp256r1.ts +2 -1
  353. package/src/primitives/Signature.ts +8 -8
  354. package/src/primitives/SymmetricKey.ts +145 -1
  355. package/src/primitives/TransactionSignature.ts +16 -16
  356. package/src/primitives/__tests/Hash.additional.test.ts +65 -0
  357. package/src/primitives/__tests/Hash.test.ts +6 -1
  358. package/src/primitives/utils.ts +37 -47
  359. package/src/registry/RegistryClient.ts +25 -25
  360. package/src/remittance/RemittanceManager.ts +17 -18
  361. package/src/remittance/modules/BasicBRC29.ts +2 -5
  362. package/src/script/Script.ts +114 -170
  363. package/src/script/ScriptEvaluationError.ts +2 -2
  364. package/src/script/Spend.ts +14 -15
  365. package/src/script/templates/PushDrop.ts +5 -3
  366. package/src/script/templates/RPuzzle.ts +2 -4
  367. package/src/storage/StorageDownloader.ts +1 -1
  368. package/src/totp/totp.ts +1 -1
  369. package/src/transaction/Beef.ts +241 -203
  370. package/src/transaction/BeefConstants.ts +16 -0
  371. package/src/transaction/BeefTx.ts +3 -3
  372. package/src/transaction/MerklePath.ts +4 -4
  373. package/src/transaction/Transaction.ts +48 -51
  374. package/src/transaction/fee-models/SatoshisPerKilobyte.ts +1 -1
  375. package/src/transaction/http/BinaryFetchClient.ts +8 -8
  376. package/src/transaction/http/DefaultHttpClient.ts +8 -8
  377. package/src/wallet/CachedKeyDeriver.ts +8 -6
  378. package/src/wallet/KeyDeriver.ts +1 -1
  379. package/src/wallet/Wallet.interfaces.ts +18 -5
  380. package/src/wallet/WalletClient.ts +9 -9
  381. package/src/wallet/WalletError.ts +1 -1
  382. package/src/wallet/__tests/WalletClient.substrate.test.ts +10 -6
  383. package/src/wallet/substrates/HTTPWalletJSON.ts +22 -21
  384. package/src/wallet/substrates/ReactNativeWebView.ts +9 -9
  385. package/src/wallet/substrates/WalletWireProcessor.ts +83 -83
  386. package/src/wallet/substrates/WalletWireTransceiver.ts +528 -938
  387. package/src/wallet/substrates/XDM.ts +4 -4
  388. package/src/wallet/substrates/__tests/HTTPWalletJSON.test.ts +38 -25
  389. package/src/wallet/substrates/__tests/ReactNativeWebView.test.ts +174 -0
  390. package/src/wallet/substrates/__tests/window.CWI.test.ts +256 -0
  391. package/src/wallet/substrates/window.CWI.ts +11 -11
  392. package/src/wallet/validationHelpers.ts +9 -9
@@ -146,7 +146,7 @@ export class GlobalKVStore {
146
146
  throw new Error('Key must be a non-empty string.')
147
147
  }
148
148
  if (typeof value !== 'string') {
149
- throw new Error('Value must be a string.')
149
+ throw new TypeError('Value must be a string.')
150
150
  }
151
151
 
152
152
  const controller = await this.getIdentityKey()
@@ -186,7 +186,30 @@ export class GlobalKVStore {
186
186
  const existingEntries = await this.queryOverlay({ key, controller }, { includeToken: true })
187
187
  const existingToken = existingEntries.length > 0 ? existingEntries[0].token : undefined
188
188
 
189
- if (existingToken != null) {
189
+ if (existingToken == null) {
190
+ // Create new token
191
+ const { tx } = await this.wallet.createAction({
192
+ description: tokenSetDescription,
193
+ outputs: [{
194
+ satoshis: tokenAmount ?? this.config.tokenAmount as number,
195
+ lockingScript: lockingScript.toHex(),
196
+ outputDescription: 'KVStore token'
197
+ }],
198
+ options: {
199
+ acceptDelayedBroadcast: this.config.acceptDelayedBroadcast,
200
+ noSend: this.config.overlayBroadcast,
201
+ randomizeOutputs: false
202
+ }
203
+ }, this.config.originator)
204
+
205
+ if (tx == null) {
206
+ throw new Error('Failed to create transaction')
207
+ }
208
+
209
+ const transaction = Transaction.fromAtomicBEEF(tx)
210
+ await this.submitToOverlay(transaction)
211
+ return `${transaction.id('hex')}.0`
212
+ } else {
190
213
  // Update existing token
191
214
  const inputs: CreateActionInput[] = [{
192
215
  outpoint: `${existingToken.txid}.${existingToken.outputIndex}`,
@@ -239,29 +262,6 @@ export class GlobalKVStore {
239
262
  const transaction = Transaction.fromAtomicBEEF(finalTx)
240
263
  await this.submitToOverlay(transaction)
241
264
  return `${transaction.id('hex')}.0`
242
- } else {
243
- // Create new token
244
- const { tx } = await this.wallet.createAction({
245
- description: tokenSetDescription,
246
- outputs: [{
247
- satoshis: tokenAmount ?? this.config.tokenAmount as number,
248
- lockingScript: lockingScript.toHex(),
249
- outputDescription: 'KVStore token'
250
- }],
251
- options: {
252
- acceptDelayedBroadcast: this.config.acceptDelayedBroadcast,
253
- noSend: this.config.overlayBroadcast,
254
- randomizeOutputs: false
255
- }
256
- }, this.config.originator)
257
-
258
- if (tx == null) {
259
- throw new Error('Failed to create transaction')
260
- }
261
-
262
- const transaction = Transaction.fromAtomicBEEF(tx)
263
- await this.submitToOverlay(transaction)
264
- return `${transaction.id('hex')}.0`
265
265
  }
266
266
  }, this.topicBroadcaster)
267
267
 
@@ -421,9 +421,7 @@ export class GlobalKVStore {
421
421
  * @private
422
422
  */
423
423
  private async getIdentityKey (): Promise<PubKeyHex> {
424
- if (this.cachedIdentityKey == null) {
425
- this.cachedIdentityKey = (await this.wallet.getPublicKey({ identityKey: true }, this.config.originator)).publicKey
426
- }
424
+ this.cachedIdentityKey ??= (await this.wallet.getPublicKey({ identityKey: true }, this.config.originator)).publicKey
427
425
  return this.cachedIdentityKey
428
426
  }
429
427
 
@@ -467,13 +465,13 @@ export class GlobalKVStore {
467
465
  const signature = decoded.fields.pop() as number[]
468
466
  try {
469
467
  await anyoneWallet.verifySignature({
470
- data: decoded.fields.reduce((a, e) => [...a, ...e], []),
468
+ data: decoded.fields.flat(),
471
469
  signature,
472
470
  counterparty: Utils.toHex(decoded.fields[kvProtocol.controller]),
473
471
  protocolID: JSON.parse(Utils.toUTF8(decoded.fields[kvProtocol.protocolID])),
474
472
  keyID: Utils.toUTF8(decoded.fields[kvProtocol.key])
475
473
  })
476
- } catch (error) {
474
+ } catch (_signatureVerificationError) {
477
475
  // Skip all outputs that fail signature verification
478
476
  continue
479
477
  }
@@ -483,7 +481,7 @@ export class GlobalKVStore {
483
481
  if (hasTagsField && decoded.fields[kvProtocol.tags] != null) {
484
482
  try {
485
483
  tags = JSON.parse(Utils.toUTF8(decoded.fields[kvProtocol.tags]))
486
- } catch (e) {
484
+ } catch (_tagsParseError) {
487
485
  // If tags parsing fails, continue without tags
488
486
  tags = undefined
489
487
  }
@@ -514,7 +512,8 @@ export class GlobalKVStore {
514
512
  }
515
513
 
516
514
  entries.push(entry)
517
- } catch (error) {
515
+ } catch (_malformedOutputError) {
516
+ // Skip malformed or undecodable outputs rather than failing the entire query
518
517
  continue
519
518
  }
520
519
  }
@@ -155,7 +155,7 @@ export default class LocalKVStore {
155
155
  if (outputs.length === 0) {
156
156
  return r
157
157
  }
158
- const output = outputs.slice(-1)[0]
158
+ const output = outputs.at(-1)!
159
159
  r.outpoint = output.outpoint
160
160
  let field: number[]
161
161
  try {
@@ -169,23 +169,23 @@ export default class LocalKVStore {
169
169
  } catch (error) {
170
170
  throw new Error(`Invalid value found. You need to call set to collapse the corrupted state (or relinquish the corrupted ${outputs[0].outpoint} output from the ${this.context} basket) before you can get this value again. Original error: ${error instanceof Error ? error.message : String(error)}`)
171
171
  }
172
- if (!this.encrypt) {
173
- r.value = Utils.toUTF8(field)
174
- } else {
172
+ if (this.encrypt) {
175
173
  const { plaintext } = await this.wallet.decrypt({
176
174
  ...this.getProtocol(key),
177
175
  ciphertext: field
178
176
  }, this.originator)
179
177
  r.value = Utils.toUTF8(plaintext)
178
+ } else {
179
+ r.value = Utils.toUTF8(field)
180
180
  }
181
181
  return r
182
182
  }
183
183
 
184
184
  private getInputs (outputs: WalletOutput[]): CreateActionInput[] {
185
185
  const inputs: CreateActionInput[] = []
186
- for (let i = 0; i < outputs.length; i++) {
186
+ for (const output of outputs) {
187
187
  inputs.push({
188
- outpoint: outputs[i].outpoint,
188
+ outpoint: output.outpoint,
189
189
  unlockingScriptLength: 74,
190
190
  inputDescription: 'Previous key-value token'
191
191
  })
@@ -197,7 +197,7 @@ export default class LocalKVStore {
197
197
  const p = this.getProtocol(key)
198
198
  const tx = Transaction.fromAtomicBEEF(atomicBEEF)
199
199
  const spends: Record<number, SignActionSpend> = {}
200
- for (let i = 0; i < outputs.length; i++) {
200
+ for (const [i] of outputs.entries()) {
201
201
  const unlocker = pushdrop.unlock(p.protocolID, p.keyID, 'self')
202
202
  const unlockingScript = await unlocker.sign(tx, i)
203
203
  spends[i] = {
@@ -328,7 +328,7 @@ export default class LocalKVStore {
328
328
  }
329
329
  }, this.originator)
330
330
  if (typeof signableTransaction !== 'object') {
331
- throw new Error('Wallet did not return a signable transaction when expected.')
331
+ throw new TypeError('Wallet did not return a signable transaction when expected.')
332
332
  }
333
333
  const spends = await this.getSpends(key, outputs, pushdrop, signableTransaction.tx)
334
334
  const { txid } = await this.wallet.signAction({
@@ -25,8 +25,8 @@ export interface KVContext { key: string, protocolID: WalletProtocol }
25
25
  export const kvStoreInterpreter: InterpreterFunction<string, KVContext> = async (transaction: Transaction, outputIndex: number, ctx?: KVContext): Promise<string | undefined> => {
26
26
  try {
27
27
  const output = transaction.outputs[outputIndex]
28
- if (output == null || output.lockingScript == null) return undefined
29
- if (ctx == null || ctx.key == null) return undefined
28
+ if (output?.lockingScript == null) return undefined
29
+ if (ctx?.key == null) return undefined
30
30
 
31
31
  // Decode the KVStore token
32
32
  const decoded = PushDrop.decode(output.lockingScript)
@@ -72,7 +72,7 @@ export const verify = (
72
72
  const verifierRest = reader.read(32)
73
73
  const verifierDER = toHex([verifierFirst, ...verifierRest])
74
74
  if (typeof recipient !== 'object') {
75
- throw new Error(
75
+ throw new TypeError(
76
76
  `This signature can only be verified with knowledge of a specific private key. The associated public key is: ${verifierDER}`
77
77
  )
78
78
  }
@@ -181,7 +181,7 @@ export class Historian<T, C = unknown> {
181
181
 
182
182
  // History is built in reverse chronological order during traversal,
183
183
  // so we reverse it to return oldest-first
184
- const chronological = history.reverse()
184
+ const chronological = history.toReversed()
185
185
 
186
186
  if (this.historyCache != null) {
187
187
  const cacheKey = this.historyKey(startTransaction, context)
@@ -1,4 +1,5 @@
1
1
  import { Transaction } from '../transaction/index.js'
2
+ import { Beef } from '../transaction/Beef.js'
2
3
  import OverlayAdminTokenTemplate from './OverlayAdminTokenTemplate.js'
3
4
  import * as Utils from '../primitives/utils.js'
4
5
  import { getOverlayHostReputationTracker, HostReputationTracker } from './HostReputationTracker.js'
@@ -35,9 +36,49 @@ export type LookupAnswer =
35
36
  beef: number[]
36
37
  outputIndex: number
37
38
  context?: number[]
39
+ /** Optional txid hint. When present, consumers can skip re-parsing beef to derive the txid. */
40
+ txid?: string
38
41
  }>
39
42
  }
40
43
 
44
+ /**
45
+ * Per-call options for {@link LookupResolver.query} and {@link LookupResolver.query$}.
46
+ * All optional; defaults preserve prior behavior.
47
+ */
48
+ export interface LookupQueryOptions {
49
+ /**
50
+ * Override the grace window (ms) between the first valid response and the resolution of the query.
51
+ * Late responders arriving within this window are merged into the result. Default 80 ms.
52
+ * Raise for identity-style paths (e.g. ~300 ms) where divergence between hosts matters.
53
+ */
54
+ graceMs?: number
55
+ /**
56
+ * Soft timeout (ms). When set:
57
+ * - `query()` resolves with whatever has arrived as soon as any host answers, or after this timeout.
58
+ * - `query$()` emits a (possibly empty) snapshot after this timeout if no host has answered yet,
59
+ * then continues yielding late-host enrichments until the iterator is broken or final emission.
60
+ */
61
+ softTimeoutMs?: number
62
+ }
63
+
64
+ /**
65
+ * One emission from {@link LookupResolver.query$}. Carries the cumulative output set discovered so far
66
+ * plus a small envelope describing progress across hosts. Callers can render fast on the first emission
67
+ * and refine in place as more hosts answer.
68
+ */
69
+ export interface LookupAnswerProgress {
70
+ type: 'output-list'
71
+ outputs: Array<{ beef: number[], outputIndex: number, context?: number[], txid?: string }>
72
+ /** Parallel array of resolved tx ids for each output (same index as `outputs`). */
73
+ txIds: string[]
74
+ /** True only for the final emission, after every in-flight host has settled. */
75
+ isFinal: boolean
76
+ /** Number of ranked hosts that were queried. */
77
+ hostCount: number
78
+ /** Number of hosts that have settled (success / fail / timeout). */
79
+ completedHosts: number
80
+ }
81
+
41
82
  /** Default SLAP trackers */
42
83
  export const DEFAULT_SLAP_TRACKERS: string[] = [
43
84
  // BSVA clusters
@@ -119,7 +160,7 @@ export class HTTPSOverlayLookupFacilitator implements OverlayLookupFacilitator {
119
160
 
120
161
  constructor (httpClient = defaultFetch, allowHTTP: boolean = false) {
121
162
  if (typeof httpClient !== 'function') {
122
- throw new Error(
163
+ throw new TypeError(
123
164
  'HTTPSOverlayLookupFacilitator requires a fetch implementation. ' +
124
165
  'In environments without fetch, provide a polyfill or custom implementation.'
125
166
  )
@@ -131,7 +172,7 @@ export class HTTPSOverlayLookupFacilitator implements OverlayLookupFacilitator {
131
172
  async lookup (
132
173
  url: string,
133
174
  question: LookupQuestion,
134
- timeout: number = 5000
175
+ timeout: number = 2000
135
176
  ): Promise<LookupAnswer> {
136
177
  if (!url.startsWith('https:') && !this.allowHTTP) {
137
178
  throw new Error(
@@ -139,7 +180,7 @@ export class HTTPSOverlayLookupFacilitator implements OverlayLookupFacilitator {
139
180
  )
140
181
  }
141
182
 
142
- const controller = typeof AbortController !== 'undefined' ? new AbortController() : undefined
183
+ const controller = typeof AbortController === 'undefined' ? undefined : new AbortController()
143
184
  const timer = setTimeout(() => {
144
185
  try { controller?.abort() } catch { /* noop */ }
145
186
  }, timeout)
@@ -158,44 +199,61 @@ export class HTTPSOverlayLookupFacilitator implements OverlayLookupFacilitator {
158
199
 
159
200
  if (!response.ok) throw new Error(`Failed to facilitate lookup (HTTP ${response.status})`)
160
201
  if (response.headers.get('content-type') === 'application/octet-stream') {
161
- const payload = await response.arrayBuffer()
162
- const r = new Utils.Reader([...new Uint8Array(payload)])
163
- const nOutpoints = r.readVarIntNum()
164
- const outpoints: Array<{ txid: string, outputIndex: number, context?: number[] }> = []
165
- for (let i = 0; i < nOutpoints; i++) {
166
- const txid = Utils.toHex(r.read(32))
167
- const outputIndex = r.readVarIntNum()
168
- const contextLength = r.readVarIntNum()
169
- let context
170
- if (contextLength > 0) {
171
- context = r.read(contextLength)
172
- }
173
- outpoints.push({
174
- txid,
175
- outputIndex,
176
- context
177
- })
178
- }
179
- const beef = r.read()
180
- return {
181
- type: 'output-list',
182
- outputs: outpoints.map(x => ({
183
- outputIndex: x.outputIndex,
184
- context: x.context,
185
- beef: Transaction.fromBEEF(beef, x.txid).toBEEF()
186
- }))
187
- }
188
- } else {
189
- return await response.json()
202
+ return await this.parseOctetStreamLookup(response)
190
203
  }
204
+ return await response.json()
191
205
  } catch (e) {
192
206
  // Normalize timeouts to a consistent error message
193
- if ((e as any)?.name === 'AbortError') throw new Error('Request timed out')
207
+ if ((e as { name?: string })?.name === 'AbortError') {
208
+ throw new Error('Request timed out')
209
+ }
194
210
  throw e
195
211
  } finally {
196
212
  clearTimeout(timer)
197
213
  }
198
214
  }
215
+
216
+ /** Parse the aggregated octet-stream lookup response into an output-list LookupAnswer. */
217
+ private async parseOctetStreamLookup (response: Response): Promise<LookupAnswer> {
218
+ const payload = await response.arrayBuffer()
219
+ const r = new Utils.Reader([...new Uint8Array(payload)])
220
+ const nOutpoints = r.readVarIntNum()
221
+ const outpoints: Array<{ txid: string, outputIndex: number, context?: number[] }> = []
222
+ for (let i = 0; i < nOutpoints; i++) {
223
+ const txid = Utils.toHex(r.read(32))
224
+ const outputIndex = r.readVarIntNum()
225
+ const contextLength = r.readVarIntNum()
226
+ const context = contextLength > 0 ? r.read(contextLength) : undefined
227
+ outpoints.push({ txid, outputIndex, context })
228
+ }
229
+ const beef = r.read()
230
+ const beefObj = Beef.fromBinary(beef)
231
+ const outputs = await this.extractAtomicOutputs(outpoints, beefObj)
232
+ return { type: 'output-list', outputs }
233
+ }
234
+
235
+ /** Memoize per-txid atomic BEEF extraction, yielding to the event loop between outputs. */
236
+ private async extractAtomicOutputs (
237
+ outpoints: Array<{ txid: string, outputIndex: number, context?: number[] }>,
238
+ beefObj: Beef
239
+ ): Promise<Array<{ outputIndex: number, context?: number[], beef: number[], txid: string }>> {
240
+ const beefByTxid = new Map<string, number[]>()
241
+ const outputs: Array<{ outputIndex: number, context?: number[], beef: number[], txid: string }> = new Array(outpoints.length)
242
+ for (let idx = 0; idx < outpoints.length; idx++) {
243
+ const x = outpoints[idx]
244
+ let beefBytes = beefByTxid.get(x.txid)
245
+ if (beefBytes === undefined) {
246
+ beefBytes = beefObj.toBinaryAtomic(x.txid)
247
+ beefByTxid.set(x.txid, beefBytes)
248
+ }
249
+ outputs[idx] = { outputIndex: x.outputIndex, context: x.context, beef: beefBytes, txid: x.txid }
250
+ // Yield to event loop so UI animations and other JS don't starve.
251
+ if (idx > 0 && idx < outpoints.length - 1) {
252
+ await new Promise<void>((resolve) => setTimeout(resolve, 0))
253
+ }
254
+ }
255
+ return outputs
256
+ }
199
257
  }
200
258
 
201
259
  /**
@@ -248,11 +306,53 @@ export default class LookupResolver {
248
306
 
249
307
  /**
250
308
  * Given a LookupQuestion, returns a LookupAnswer. Aggregates across multiple services and supports resiliency.
309
+ *
310
+ * Optional `options.graceMs` overrides the per-call grace window (default 80 ms).
311
+ * Optional `options.softTimeoutMs` resolves the query early with whatever has arrived once any host has
312
+ * answered (or with an empty result if no host has answered by `softTimeoutMs`).
251
313
  */
252
314
  async query (
253
315
  question: LookupQuestion,
254
- timeout?: number
316
+ timeout?: number,
317
+ options?: LookupQueryOptions
255
318
  ): Promise<LookupAnswer> {
319
+ // Existing fast-but-narrow contract: return at the first cumulative emission
320
+ // (the post-grace aggregate, or the final emission when every host settles
321
+ // before the grace window). Callers wanting progressive enrichment use query$().
322
+ // Take only the first emission, then explicitly close the iterator so the
323
+ // generator's `finally` block runs and clears any outstanding timers.
324
+ const iter = this.query$(question, timeout, options)[Symbol.asyncIterator]()
325
+ let last: LookupAnswerProgress | null = null
326
+ try {
327
+ const { value, done } = await iter.next()
328
+ if (done !== true && value != null) last = value
329
+ } finally {
330
+ await iter.return?.(undefined)
331
+ }
332
+ return {
333
+ type: 'output-list',
334
+ outputs: last?.outputs ?? []
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Iterable form of {@link query}. Emits partial results as hosts answer.
340
+ *
341
+ * Emission order:
342
+ * - First emission: after the grace window expires (or as soon as the soft timeout elapses), containing
343
+ * every output gathered from hosts that answered by then.
344
+ * - Subsequent emissions: re-emitted whenever a late host returns extra outputs that weren't in earlier
345
+ * emissions. Each emission contains the cumulative `outputs` set.
346
+ * - Final emission: `isFinal: true` once all in-flight hosts have settled (success / fail / timeout). The
347
+ * caller can `break` early; outstanding work is bounded by the per-host timeout.
348
+ *
349
+ * No host work runs past its per-host `timeout` — there is no leak risk on early break.
350
+ */
351
+ async * query$ (
352
+ question: LookupQuestion,
353
+ timeout?: number,
354
+ options?: LookupQueryOptions
355
+ ): AsyncIterable<LookupAnswerProgress> {
256
356
  let competentHosts: string[] = []
257
357
  if (question.service === 'ls_slap') {
258
358
  competentHosts = this.networkPreset === 'local' ? ['http://localhost:8080'] : this.slapTrackers
@@ -282,69 +382,116 @@ export default class LookupResolver {
282
382
  throw new Error(`All competent hosts for ${question.service} are temporarily unavailable due to backoff.`)
283
383
  }
284
384
 
285
- // Fire all hosts; resolve as soon as we have results from any host,
286
- // then allow a short grace window for additional hosts to contribute.
287
- const GRACE_MS = 80
288
- const answers: LookupAnswer[] = await new Promise<LookupAnswer[]>((resolve) => {
289
- const collected: LookupAnswer[] = []
290
- let pending = rankedHosts.length
291
- let graceTimer: ReturnType<typeof setTimeout> | null = null
292
-
293
- const tryResolve = (): void => {
294
- if (graceTimer !== null) clearTimeout(graceTimer)
295
- resolve(collected)
296
- }
385
+ const graceMs = options?.graceMs ?? 80
386
+ const softTimeoutMs = options?.softTimeoutMs
297
387
 
298
- for (const host of rankedHosts) {
299
- this.lookupHostWithTracking(host, question, timeout)
300
- .then((answer) => {
301
- if (answer?.type === 'output-list' && Array.isArray(answer.outputs) && answer.outputs.length > 0) {
302
- collected.push(answer)
303
- // First valid response: start a grace window for others
304
- if (collected.length === 1 && pending > 1) {
305
- graceTimer = setTimeout(tryResolve, GRACE_MS)
306
- }
307
- }
308
- })
309
- .catch(() => { /* host failed; tracked by lookupHostWithTracking */ })
310
- .finally(() => {
311
- pending--
312
- if (pending === 0) tryResolve()
313
- })
388
+ const hostCount = rankedHosts.length
389
+ const outputsMap = new Map<string, { beef: number[], context?: number[], outputIndex: number }>()
390
+ const txIds: string[] = []
391
+ let completedHosts = 0
392
+ let firstResponseAt: number | null = null
393
+
394
+ type Event = { kind: 'answer', answer: LookupAnswer } | { kind: 'done' } | { kind: 'soft' }
395
+ const queue: Event[] = []
396
+ let waiter: ((v: void) => void) | null = null
397
+ const push = (e: Event): void => {
398
+ queue.push(e)
399
+ if (waiter !== null) {
400
+ const w = waiter
401
+ waiter = null
402
+ w()
314
403
  }
315
- })
404
+ }
316
405
 
317
- const outputsMap = new Map<string, { beef: number[], context?: number[], outputIndex: number }>()
406
+ for (const host of rankedHosts) {
407
+ this.lookupHostWithTracking(host, question, timeout)
408
+ .then((answer) => {
409
+ if (answer?.type === 'output-list' && Array.isArray(answer.outputs) && answer.outputs.length > 0) {
410
+ push({ kind: 'answer', answer })
411
+ }
412
+ })
413
+ .catch(() => { /* tracked already */ })
414
+ .finally(() => {
415
+ completedHosts++
416
+ push({ kind: 'done' })
417
+ })
418
+ }
318
419
 
319
- // Memo key helper for tx parsing
320
- const beefKey = (beef: number[]): string => {
321
- if (typeof beef !== 'object') return '' // The invalid BEEF has an empty key.
322
- return beef.join(',')
420
+ let softTimer: ReturnType<typeof setTimeout> | null = null
421
+ if (typeof softTimeoutMs === 'number' && softTimeoutMs >= 0) {
422
+ softTimer = setTimeout(() => push({ kind: 'soft' }), softTimeoutMs)
323
423
  }
324
424
 
325
- for (const response of answers) {
326
- for (const output of response.outputs) {
327
- const keyForBeef = beefKey(output.beef)
328
- let memo = this.txMemo.get(keyForBeef)
329
- const now = Date.now()
330
- if (typeof memo !== 'object' || memo === null || memo.expiresAt <= now) {
331
- try {
332
- const txId = Transaction.fromBEEF(output.beef).id('hex')
333
- memo = { txId, expiresAt: now + this.txMemoTtlMs }
334
- if (this.txMemo.size > 4096) this.evictOldest(this.txMemo)
335
- this.txMemo.set(keyForBeef, memo)
336
- } catch {
337
- continue
338
- }
425
+ let graceTimer: ReturnType<typeof setTimeout> | null = null
426
+ let graceFired = false
427
+ let emittedOnce = false
428
+
429
+ const mergeAnswer = (answer: LookupAnswer): boolean => {
430
+ let added = false
431
+ const now = Date.now()
432
+ for (const output of answer.outputs) {
433
+ const txId = this.resolveTxIdForOutput(output, now)
434
+ if (txId === null) continue
435
+ const uniqKey = `${txId}.${output.outputIndex}`
436
+ if (!outputsMap.has(uniqKey)) {
437
+ outputsMap.set(uniqKey, output)
438
+ txIds.push(txId)
439
+ added = true
339
440
  }
340
-
341
- const uniqKey = `${memo.txId}.${output.outputIndex}`
342
- outputsMap.set(uniqKey, output)
343
441
  }
442
+ return added
344
443
  }
345
- return {
444
+
445
+ const snapshot = (isFinal: boolean): LookupAnswerProgress => ({
346
446
  type: 'output-list',
347
- outputs: Array.from(outputsMap.values())
447
+ outputs: Array.from(outputsMap.values()),
448
+ txIds: txIds.slice(),
449
+ isFinal,
450
+ hostCount,
451
+ completedHosts
452
+ })
453
+
454
+ try {
455
+ while (completedHosts < hostCount) {
456
+ if (queue.length === 0) {
457
+ await new Promise<void>((resolve) => { waiter = resolve })
458
+ }
459
+ const e = queue.shift() as Event
460
+ if (e.kind === 'answer') {
461
+ const added = mergeAnswer(e.answer)
462
+ if (firstResponseAt === null) {
463
+ firstResponseAt = Date.now()
464
+ if (!graceFired && graceMs > 0) {
465
+ graceTimer = setTimeout(() => {
466
+ graceFired = true
467
+ push({ kind: 'soft' })
468
+ }, graceMs)
469
+ } else {
470
+ graceFired = true
471
+ }
472
+ }
473
+ if (graceFired && added) {
474
+ emittedOnce = true
475
+ yield snapshot(false)
476
+ }
477
+ } else if (e.kind === 'soft') {
478
+ if (!emittedOnce) {
479
+ graceFired = true
480
+ emittedOnce = true
481
+ yield snapshot(false)
482
+ }
483
+ if (typeof softTimeoutMs === 'number' && firstResponseAt !== null) {
484
+ // Soft timeout: caller asked to bail out once any answer is in. Yield final.
485
+ break
486
+ }
487
+ } else if (e.kind === 'done') {
488
+ // continue loop; final emission happens after the loop
489
+ }
490
+ }
491
+ yield snapshot(true)
492
+ } finally {
493
+ if (graceTimer !== null) clearTimeout(graceTimer)
494
+ if (softTimer !== null) clearTimeout(softTimer)
348
495
  }
349
496
  }
350
497
 
@@ -375,7 +522,7 @@ export default class LookupResolver {
375
522
  try {
376
523
  const hosts = await this.hostsInFlight.get(service)
377
524
  if (typeof hosts !== 'object') {
378
- throw new Error('Hosts is not defined.')
525
+ throw new TypeError('Hosts is not defined.')
379
526
  }
380
527
  return hosts.slice()
381
528
  } catch {
@@ -466,7 +613,7 @@ export default class LookupResolver {
466
613
  resolve([...allHosts])
467
614
  }
468
615
  })
469
- .catch(() => { /* tracker failed; tracked by lookupHostWithTracking */ })
616
+ .catch(() => { /* tracker failure tracked in reputation */ })
470
617
  .finally(() => {
471
618
  pending--
472
619
  if (pending === 0 && !resolved) {
@@ -478,7 +625,34 @@ export default class LookupResolver {
478
625
  })
479
626
  }
480
627
 
481
- /** Evict an arbitrary “oldest” entry from a Map (iteration order). */
628
+ /**
629
+ * Resolve a txid for an aggregated lookup output. Uses the threaded-through `output.txid`
630
+ * fast path when present; otherwise memoizes Transaction.fromBEEF(beef).id('hex') keyed by
631
+ * the BEEF byte sequence. Returns null when the BEEF is unparseable.
632
+ */
633
+ private resolveTxIdForOutput (
634
+ output: { txid?: string, beef: number[], outputIndex: number, context?: number[] },
635
+ now: number
636
+ ): string | null {
637
+ if (typeof output.txid === 'string' && output.txid.length > 0) {
638
+ return output.txid
639
+ }
640
+ const keyForBeef = Array.isArray(output.beef) ? output.beef.join(',') : ''
641
+ const memo = this.txMemo.get(keyForBeef)
642
+ if (typeof memo === 'object' && memo !== null && memo.expiresAt > now) {
643
+ return memo.txId
644
+ }
645
+ try {
646
+ const txId = Transaction.fromBEEF(output.beef).id('hex')
647
+ if (this.txMemo.size > 4096) this.evictOldest(this.txMemo)
648
+ this.txMemo.set(keyForBeef, { txId, expiresAt: now + this.txMemoTtlMs })
649
+ return txId
650
+ } catch {
651
+ return null
652
+ }
653
+ }
654
+
655
+ /** Evict an arbitrary "oldest" entry from a Map (iteration order). */
482
656
  private evictOldest<T>(m: Map<string, T>): void {
483
657
  const firstKey = m.keys().next().value
484
658
  if (firstKey !== undefined) m.delete(firstKey)