@geeqdev/geeq-sdk 0.1.0 → 0.2.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 (392) hide show
  1. package/.idea/geeq_sdk.iml +12 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/vcs.xml +6 -0
  4. package/.nvmrc +1 -0
  5. package/CONTRIBUTING.md +20 -0
  6. package/dist/src/address/record-type.d.ts +10 -0
  7. package/dist/src/address/record-type.js +59 -0
  8. package/dist/src/address/record-type.js.map +1 -0
  9. package/dist/src/address/wallet-address.d.ts +34 -0
  10. package/dist/src/address/wallet-address.js +90 -0
  11. package/dist/src/address/wallet-address.js.map +1 -0
  12. package/dist/src/amount.d.ts +43 -0
  13. package/dist/src/amount.js +105 -0
  14. package/dist/src/amount.js.map +1 -0
  15. package/dist/src/assert.d.ts +12 -0
  16. package/dist/src/assert.js +24 -0
  17. package/dist/src/assert.js.map +1 -0
  18. package/dist/src/capnp/adminNetTx.capnp.d.ts +75 -0
  19. package/dist/src/capnp/adminNetTx.capnp.js +66 -0
  20. package/dist/src/capnp/adminNetTx.capnp.js.map +1 -0
  21. package/dist/src/capnp/appLayBlock.capnp.d.ts +26 -0
  22. package/dist/src/capnp/appLayBlock.capnp.js +24 -0
  23. package/dist/src/capnp/appLayBlock.capnp.js.map +1 -0
  24. package/dist/src/capnp/appLayLedger.capnp.d.ts +34 -0
  25. package/dist/src/capnp/appLayLedger.capnp.js +32 -0
  26. package/dist/src/capnp/appLayLedger.capnp.js.map +1 -0
  27. package/dist/src/capnp/appLayRec.capnp.d.ts +129 -0
  28. package/dist/src/capnp/appLayRec.capnp.js +156 -0
  29. package/dist/src/capnp/appLayRec.capnp.js.map +1 -0
  30. package/dist/src/capnp/chainStateView.capnp.d.ts +50 -0
  31. package/dist/src/capnp/chainStateView.capnp.js +48 -0
  32. package/dist/src/capnp/chainStateView.capnp.js.map +1 -0
  33. package/dist/src/capnp/coinAcctRec.capnp.d.ts +178 -0
  34. package/dist/src/capnp/coinAcctRec.capnp.js +189 -0
  35. package/dist/src/capnp/coinAcctRec.capnp.js.map +1 -0
  36. package/dist/src/capnp/genesisBlock.capnp.d.ts +61 -0
  37. package/dist/src/capnp/genesisBlock.capnp.js +55 -0
  38. package/dist/src/capnp/genesisBlock.capnp.js.map +1 -0
  39. package/dist/src/capnp/hubContentStmt.capnp.d.ts +63 -0
  40. package/dist/src/capnp/hubContentStmt.capnp.js +57 -0
  41. package/dist/src/capnp/hubContentStmt.capnp.js.map +1 -0
  42. package/dist/src/capnp/misc.capnp.d.ts +20 -0
  43. package/dist/src/capnp/misc.capnp.js +18 -0
  44. package/dist/src/capnp/misc.capnp.js.map +1 -0
  45. package/dist/src/capnp/netActorRec.capnp.d.ts +65 -0
  46. package/dist/src/capnp/netActorRec.capnp.js +65 -0
  47. package/dist/src/capnp/netActorRec.capnp.js.map +1 -0
  48. package/dist/src/capnp/nodeContentStmt.capnp.d.ts +67 -0
  49. package/dist/src/capnp/nodeContentStmt.capnp.js +61 -0
  50. package/dist/src/capnp/nodeContentStmt.capnp.js.map +1 -0
  51. package/dist/src/capnp/nodePayloadList.capnp.d.ts +28 -0
  52. package/dist/src/capnp/nodePayloadList.capnp.js +26 -0
  53. package/dist/src/capnp/nodePayloadList.capnp.js.map +1 -0
  54. package/dist/src/capnp/nodeTxList.capnp.d.ts +36 -0
  55. package/dist/src/capnp/nodeTxList.capnp.js +34 -0
  56. package/dist/src/capnp/nodeTxList.capnp.js.map +1 -0
  57. package/dist/src/capnp/paramAcctRec.capnp.d.ts +338 -0
  58. package/dist/src/capnp/paramAcctRec.capnp.js +669 -0
  59. package/dist/src/capnp/paramAcctRec.capnp.js.map +1 -0
  60. package/dist/src/capnp/payload.capnp.d.ts +237 -0
  61. package/dist/src/capnp/payload.capnp.js +260 -0
  62. package/dist/src/capnp/payload.capnp.js.map +1 -0
  63. package/dist/src/capnp/proxyAcctRec.capnp.d.ts +223 -0
  64. package/dist/src/capnp/proxyAcctRec.capnp.js +255 -0
  65. package/dist/src/capnp/proxyAcctRec.capnp.js.map +1 -0
  66. package/dist/src/capnp/relayContentStmt.capnp.d.ts +82 -0
  67. package/dist/src/capnp/relayContentStmt.capnp.js +85 -0
  68. package/dist/src/capnp/relayContentStmt.capnp.js.map +1 -0
  69. package/dist/src/capnp/sysAcctRec.capnp.d.ts +48 -0
  70. package/dist/src/capnp/sysAcctRec.capnp.js +46 -0
  71. package/dist/src/capnp/sysAcctRec.capnp.js.map +1 -0
  72. package/dist/src/capnp/userAppTx.capnp.d.ts +230 -0
  73. package/dist/src/capnp/userAppTx.capnp.js +270 -0
  74. package/dist/src/capnp/userAppTx.capnp.js.map +1 -0
  75. package/dist/src/capnp/userValTx.capnp.d.ts +1575 -0
  76. package/dist/src/capnp/userValTx.capnp.js +2004 -0
  77. package/dist/src/capnp/userValTx.capnp.js.map +1 -0
  78. package/dist/src/capnp/valLayBlock.capnp.d.ts +88 -0
  79. package/dist/src/capnp/valLayBlock.capnp.js +86 -0
  80. package/dist/src/capnp/valLayBlock.capnp.js.map +1 -0
  81. package/dist/src/capnp/valLayLedger.capnp.d.ts +85 -0
  82. package/dist/src/capnp/valLayLedger.capnp.js +75 -0
  83. package/dist/src/capnp/valLayLedger.capnp.js.map +1 -0
  84. package/dist/src/chain/geeq-chain.d.ts +474 -0
  85. package/dist/src/chain/geeq-chain.js +721 -0
  86. package/dist/src/chain/geeq-chain.js.map +1 -0
  87. package/dist/src/chain/geeq-node.d.ts +57 -0
  88. package/dist/src/chain/geeq-node.js +62 -0
  89. package/dist/src/chain/geeq-node.js.map +1 -0
  90. package/dist/src/chain/nonce-guard.d.ts +42 -0
  91. package/dist/src/chain/nonce-guard.js +68 -0
  92. package/dist/src/chain/nonce-guard.js.map +1 -0
  93. package/dist/src/chain/select.d.ts +40 -0
  94. package/dist/src/chain/select.js +114 -0
  95. package/dist/src/chain/select.js.map +1 -0
  96. package/dist/src/crypto/account-id.d.ts +35 -0
  97. package/dist/src/crypto/account-id.js +58 -0
  98. package/dist/src/crypto/account-id.js.map +1 -0
  99. package/dist/src/crypto/keypair.d.ts +24 -0
  100. package/dist/src/crypto/keypair.js +158 -0
  101. package/dist/src/crypto/keypair.js.map +1 -0
  102. package/dist/src/crypto/mnemonic.d.ts +34 -0
  103. package/dist/src/crypto/mnemonic.js +57 -0
  104. package/dist/src/crypto/mnemonic.js.map +1 -0
  105. package/dist/src/encoding/base64.d.ts +8 -0
  106. package/dist/src/encoding/base64.js +53 -0
  107. package/dist/src/encoding/base64.js.map +1 -0
  108. package/dist/src/encoding/hex.d.ts +2 -0
  109. package/dist/src/encoding/hex.js +35 -0
  110. package/dist/src/encoding/hex.js.map +1 -0
  111. package/dist/src/errors.d.ts +83 -0
  112. package/dist/src/errors.js +49 -0
  113. package/dist/src/errors.js.map +1 -0
  114. package/dist/src/index.d.ts +59 -0
  115. package/dist/src/index.js +53 -0
  116. package/dist/src/index.js.map +1 -0
  117. package/dist/src/ledger/coin-accounts.d.ts +42 -0
  118. package/dist/src/ledger/coin-accounts.js +37 -0
  119. package/dist/src/ledger/coin-accounts.js.map +1 -0
  120. package/dist/src/ledger/decode.d.ts +18 -0
  121. package/dist/src/ledger/decode.js +113 -0
  122. package/dist/src/ledger/decode.js.map +1 -0
  123. package/dist/src/ledger/geeq-ledger.d.ts +99 -0
  124. package/dist/src/ledger/geeq-ledger.js +125 -0
  125. package/dist/src/ledger/geeq-ledger.js.map +1 -0
  126. package/dist/src/ledger/net-actors.d.ts +65 -0
  127. package/dist/src/ledger/net-actors.js +53 -0
  128. package/dist/src/ledger/net-actors.js.map +1 -0
  129. package/dist/src/ledger/nft-records.d.ts +13 -0
  130. package/dist/src/ledger/nft-records.js +41 -0
  131. package/dist/src/ledger/nft-records.js.map +1 -0
  132. package/dist/src/ledger/params.d.ts +34 -0
  133. package/dist/src/ledger/params.js +71 -0
  134. package/dist/src/ledger/params.js.map +1 -0
  135. package/dist/src/ledger/proxy-accounts.d.ts +26 -0
  136. package/dist/src/ledger/proxy-accounts.js +27 -0
  137. package/dist/src/ledger/proxy-accounts.js.map +1 -0
  138. package/dist/src/net/discovery.d.ts +62 -0
  139. package/dist/src/net/discovery.js +136 -0
  140. package/dist/src/net/discovery.js.map +1 -0
  141. package/dist/src/net/explorer.d.ts +57 -0
  142. package/dist/src/net/explorer.js +64 -0
  143. package/dist/src/net/explorer.js.map +1 -0
  144. package/dist/src/net/read-ledger.d.ts +7 -0
  145. package/dist/src/net/read-ledger.js +39 -0
  146. package/dist/src/net/read-ledger.js.map +1 -0
  147. package/dist/src/net/routes.d.ts +49 -0
  148. package/dist/src/net/routes.js +70 -0
  149. package/dist/src/net/routes.js.map +1 -0
  150. package/dist/src/net/transport.d.ts +55 -0
  151. package/dist/src/net/transport.js +168 -0
  152. package/dist/src/net/transport.js.map +1 -0
  153. package/dist/src/net/websocket.d.ts +49 -0
  154. package/dist/src/net/websocket.js +54 -0
  155. package/dist/src/net/websocket.js.map +1 -0
  156. package/dist/src/runtime/bindings.d.ts +9 -0
  157. package/dist/src/runtime/bindings.js +27 -0
  158. package/dist/src/runtime/bindings.js.map +1 -0
  159. package/dist/src/runtime/init.d.ts +13 -0
  160. package/dist/src/runtime/init.js +40 -0
  161. package/dist/src/runtime/init.js.map +1 -0
  162. package/dist/src/runtime/wasm-loader.browser.d.ts +14 -0
  163. package/dist/src/runtime/wasm-loader.browser.js +20 -0
  164. package/dist/src/runtime/wasm-loader.browser.js.map +1 -0
  165. package/dist/src/runtime/wasm-loader.node.d.ts +7 -0
  166. package/dist/src/runtime/wasm-loader.node.js +14 -0
  167. package/dist/src/runtime/wasm-loader.node.js.map +1 -0
  168. package/dist/src/socket/geeq-socket.d.ts +232 -0
  169. package/dist/src/socket/geeq-socket.js +634 -0
  170. package/dist/src/socket/geeq-socket.js.map +1 -0
  171. package/dist/src/socket/tracked-tx.d.ts +148 -0
  172. package/dist/src/socket/tracked-tx.js +212 -0
  173. package/dist/src/socket/tracked-tx.js.map +1 -0
  174. package/dist/src/socket/tx-status.d.ts +69 -0
  175. package/dist/src/socket/tx-status.js +72 -0
  176. package/dist/src/socket/tx-status.js.map +1 -0
  177. package/dist/src/tx/attestation.d.ts +60 -0
  178. package/dist/src/tx/attestation.js +163 -0
  179. package/dist/src/tx/attestation.js.map +1 -0
  180. package/dist/src/tx/content.d.ts +27 -0
  181. package/dist/src/tx/content.js +62 -0
  182. package/dist/src/tx/content.js.map +1 -0
  183. package/dist/src/tx/encode.d.ts +60 -0
  184. package/dist/src/tx/encode.js +82 -0
  185. package/dist/src/tx/encode.js.map +1 -0
  186. package/dist/src/tx/expiry.d.ts +11 -0
  187. package/dist/src/tx/expiry.js +12 -0
  188. package/dist/src/tx/expiry.js.map +1 -0
  189. package/dist/src/tx/fee.d.ts +16 -0
  190. package/dist/src/tx/fee.js +29 -0
  191. package/dist/src/tx/fee.js.map +1 -0
  192. package/dist/src/tx/metadata.d.ts +39 -0
  193. package/dist/src/tx/metadata.js +66 -0
  194. package/dist/src/tx/metadata.js.map +1 -0
  195. package/dist/src/tx/nft.d.ts +16 -0
  196. package/dist/src/tx/nft.js +98 -0
  197. package/dist/src/tx/nft.js.map +1 -0
  198. package/dist/src/tx/proxy.d.ts +37 -0
  199. package/dist/src/tx/proxy.js +134 -0
  200. package/dist/src/tx/proxy.js.map +1 -0
  201. package/dist/src/tx/transfer.d.ts +31 -0
  202. package/dist/src/tx/transfer.js +123 -0
  203. package/dist/src/tx/transfer.js.map +1 -0
  204. package/dist/src/types.d.ts +448 -0
  205. package/dist/src/types.js +2 -0
  206. package/dist/src/types.js.map +1 -0
  207. package/dist/tests/account-id.test.d.ts +1 -0
  208. package/dist/tests/account-id.test.js +27 -0
  209. package/dist/tests/account-id.test.js.map +1 -0
  210. package/dist/tests/amount.test.d.ts +1 -0
  211. package/dist/tests/amount.test.js +113 -0
  212. package/dist/tests/amount.test.js.map +1 -0
  213. package/dist/tests/attestation.test.d.ts +1 -0
  214. package/dist/tests/attestation.test.js +182 -0
  215. package/dist/tests/attestation.test.js.map +1 -0
  216. package/dist/tests/chain.test.d.ts +1 -0
  217. package/dist/tests/chain.test.js +647 -0
  218. package/dist/tests/chain.test.js.map +1 -0
  219. package/dist/tests/content.test.d.ts +1 -0
  220. package/dist/tests/content.test.js +90 -0
  221. package/dist/tests/content.test.js.map +1 -0
  222. package/dist/tests/documented-api.test.d.ts +1 -0
  223. package/dist/tests/documented-api.test.js +229 -0
  224. package/dist/tests/documented-api.test.js.map +1 -0
  225. package/dist/tests/explorer.test.d.ts +1 -0
  226. package/dist/tests/explorer.test.js +95 -0
  227. package/dist/tests/explorer.test.js.map +1 -0
  228. package/dist/tests/fee.test.d.ts +1 -0
  229. package/dist/tests/fee.test.js +179 -0
  230. package/dist/tests/fee.test.js.map +1 -0
  231. package/dist/tests/fixtures/build-vll.d.ts +71 -0
  232. package/dist/tests/fixtures/build-vll.js +141 -0
  233. package/dist/tests/fixtures/build-vll.js.map +1 -0
  234. package/dist/tests/fixtures/fake-socket.d.ts +44 -0
  235. package/dist/tests/fixtures/fake-socket.js +93 -0
  236. package/dist/tests/fixtures/fake-socket.js.map +1 -0
  237. package/dist/tests/fixtures/fake-transport.d.ts +24 -0
  238. package/dist/tests/fixtures/fake-transport.js +30 -0
  239. package/dist/tests/fixtures/fake-transport.js.map +1 -0
  240. package/dist/tests/keypair.test.d.ts +1 -0
  241. package/dist/tests/keypair.test.js +163 -0
  242. package/dist/tests/keypair.test.js.map +1 -0
  243. package/dist/tests/ledger.test.d.ts +1 -0
  244. package/dist/tests/ledger.test.js +234 -0
  245. package/dist/tests/ledger.test.js.map +1 -0
  246. package/dist/tests/mnemonic.test.d.ts +1 -0
  247. package/dist/tests/mnemonic.test.js +97 -0
  248. package/dist/tests/mnemonic.test.js.map +1 -0
  249. package/dist/tests/nft-transfer.test.d.ts +1 -0
  250. package/dist/tests/nft-transfer.test.js +170 -0
  251. package/dist/tests/nft-transfer.test.js.map +1 -0
  252. package/dist/tests/nft.test.d.ts +1 -0
  253. package/dist/tests/nft.test.js +180 -0
  254. package/dist/tests/nft.test.js.map +1 -0
  255. package/dist/tests/offline.test.d.ts +1 -0
  256. package/dist/tests/offline.test.js +95 -0
  257. package/dist/tests/offline.test.js.map +1 -0
  258. package/dist/tests/proxy.test.d.ts +1 -0
  259. package/dist/tests/proxy.test.js +183 -0
  260. package/dist/tests/proxy.test.js.map +1 -0
  261. package/dist/tests/select.test.d.ts +1 -0
  262. package/dist/tests/select.test.js +200 -0
  263. package/dist/tests/select.test.js.map +1 -0
  264. package/dist/tests/socket.test.d.ts +1 -0
  265. package/dist/tests/socket.test.js +607 -0
  266. package/dist/tests/socket.test.js.map +1 -0
  267. package/dist/tests/transfer.test.d.ts +1 -0
  268. package/dist/tests/transfer.test.js +225 -0
  269. package/dist/tests/transfer.test.js.map +1 -0
  270. package/dist/tests/wallet-address.test.d.ts +1 -0
  271. package/dist/tests/wallet-address.test.js +101 -0
  272. package/dist/tests/wallet-address.test.js.map +1 -0
  273. package/dist/tests/wasm-loader.jest.d.ts +13 -0
  274. package/dist/tests/wasm-loader.jest.js +15 -0
  275. package/dist/tests/wasm-loader.jest.js.map +1 -0
  276. package/example/.env.example +4 -0
  277. package/example/README.md +304 -0
  278. package/example/USAGE.md +74 -0
  279. package/example/index.html +12 -0
  280. package/example/package-lock.json +1557 -0
  281. package/example/package.json +25 -0
  282. package/example/scripts/browser-check.mjs +1421 -0
  283. package/example/scripts/match-derivation.mjs +65 -0
  284. package/example/src/App.vue +138 -0
  285. package/example/src/components/AuthorityPanel.vue +277 -0
  286. package/example/src/components/ChainPanel.vue +267 -0
  287. package/example/src/components/ChainState.vue +59 -0
  288. package/example/src/components/CopyButton.vue +25 -0
  289. package/example/src/components/CreateAssetPanel.vue +313 -0
  290. package/example/src/components/DigitalAssetsPanel.vue +68 -0
  291. package/example/src/components/EvidencePanel.vue +396 -0
  292. package/example/src/components/ExplorerPanel.vue +119 -0
  293. package/example/src/components/KeypairPanel.vue +182 -0
  294. package/example/src/components/PhrasePanel.vue +105 -0
  295. package/example/src/components/SocketWarning.vue +18 -0
  296. package/example/src/components/StepGate.vue +24 -0
  297. package/example/src/components/TransferPanel.vue +416 -0
  298. package/example/src/components/TxDeadline.vue +19 -0
  299. package/example/src/components/TxFee.vue +31 -0
  300. package/example/src/components/TxStatus.vue +105 -0
  301. package/example/src/composables/useSocketConnection.ts +80 -0
  302. package/example/src/env.d.ts +12 -0
  303. package/example/src/main.ts +5 -0
  304. package/example/src/pennies.ts +7 -0
  305. package/example/src/style.css +149 -0
  306. package/example/tsconfig.json +19 -0
  307. package/example/vite.config.ts +26 -0
  308. package/jest.config.js +18 -0
  309. package/package.json +2 -6
  310. package/scripts/copy-wasm.mjs +21 -0
  311. package/scripts/smoke.mjs +382 -0
  312. package/src/address/record-type.ts +77 -0
  313. package/src/address/wallet-address.ts +105 -0
  314. package/src/amount.ts +143 -0
  315. package/src/assert.ts +30 -0
  316. package/src/capnp/README.md +15 -0
  317. package/src/capnp/adminNetTx.capnp.ts +66 -0
  318. package/src/capnp/appLayBlock.capnp.ts +26 -0
  319. package/src/capnp/appLayLedger.capnp.ts +35 -0
  320. package/src/capnp/appLayRec.capnp.ts +159 -0
  321. package/src/capnp/chainStateView.capnp.ts +49 -0
  322. package/src/capnp/coinAcctRec.capnp.ts +193 -0
  323. package/src/capnp/genesisBlock.capnp.ts +56 -0
  324. package/src/capnp/hubContentStmt.capnp.ts +59 -0
  325. package/src/capnp/misc.capnp.ts +19 -0
  326. package/src/capnp/netActorRec.capnp.ts +64 -0
  327. package/src/capnp/nodeContentStmt.capnp.ts +62 -0
  328. package/src/capnp/nodePayloadList.capnp.ts +28 -0
  329. package/src/capnp/nodeTxList.capnp.ts +37 -0
  330. package/src/capnp/paramAcctRec.capnp.ts +669 -0
  331. package/src/capnp/payload.capnp.ts +261 -0
  332. package/src/capnp/proxyAcctRec.capnp.ts +259 -0
  333. package/src/capnp/relayContentStmt.capnp.ts +85 -0
  334. package/src/capnp/sysAcctRec.capnp.ts +47 -0
  335. package/src/capnp/userAppTx.capnp.ts +269 -0
  336. package/src/capnp/userValTx.capnp.ts +1999 -0
  337. package/src/capnp/valLayBlock.capnp.ts +93 -0
  338. package/src/capnp/valLayLedger.capnp.ts +80 -0
  339. package/src/chain/geeq-chain.ts +1094 -0
  340. package/src/chain/geeq-node.ts +105 -0
  341. package/src/chain/nonce-guard.ts +73 -0
  342. package/src/chain/select.ts +149 -0
  343. package/src/crypto/account-id.ts +83 -0
  344. package/src/crypto/keypair.ts +221 -0
  345. package/src/crypto/mnemonic.ts +69 -0
  346. package/src/encoding/base64.ts +58 -0
  347. package/src/encoding/hex.ts +42 -0
  348. package/src/errors.ts +122 -0
  349. package/src/index.ts +195 -0
  350. package/src/ledger/coin-accounts.ts +73 -0
  351. package/src/ledger/decode.ts +147 -0
  352. package/src/ledger/geeq-ledger.ts +184 -0
  353. package/src/ledger/net-actors.ts +129 -0
  354. package/src/ledger/nft-records.ts +49 -0
  355. package/src/ledger/params.ts +82 -0
  356. package/src/ledger/proxy-accounts.ts +51 -0
  357. package/src/net/discovery.ts +231 -0
  358. package/src/net/explorer.ts +123 -0
  359. package/src/net/read-ledger.ts +58 -0
  360. package/src/net/routes.ts +79 -0
  361. package/src/net/transport.ts +221 -0
  362. package/src/net/websocket.ts +94 -0
  363. package/src/runtime/bindings.ts +38 -0
  364. package/src/runtime/init.ts +49 -0
  365. package/src/runtime/wasm-loader.browser.ts +22 -0
  366. package/src/runtime/wasm-loader.node.ts +16 -0
  367. package/src/socket/geeq-socket.ts +827 -0
  368. package/src/socket/tracked-tx.ts +319 -0
  369. package/src/socket/tx-status.ts +131 -0
  370. package/src/tx/attestation.ts +222 -0
  371. package/src/tx/content.ts +82 -0
  372. package/src/tx/encode.ts +107 -0
  373. package/src/tx/expiry.ts +11 -0
  374. package/src/tx/fee.ts +31 -0
  375. package/src/tx/metadata.ts +100 -0
  376. package/src/tx/nft.ts +108 -0
  377. package/src/tx/proxy.ts +156 -0
  378. package/src/tx/transfer.ts +162 -0
  379. package/src/types.ts +503 -0
  380. package/src/wasm/README.md +22 -0
  381. package/src/wasm/browser/geeq_core.d.ts +1224 -0
  382. package/src/wasm/browser/geeq_core.js +6014 -0
  383. package/src/wasm/browser/geeq_core_bg.wasm +0 -0
  384. package/src/wasm/browser/geeq_core_bg.wasm.d.ts +447 -0
  385. package/src/wasm/nodejs/geeq_core.d.ts +753 -0
  386. package/src/wasm/nodejs/geeq_core.js +6016 -0
  387. package/src/wasm/nodejs/geeq_core_bg.wasm +0 -0
  388. package/src/wasm/nodejs/geeq_core_bg.wasm.d.ts +447 -0
  389. package/src/wasm/nodejs/package.json +3 -0
  390. package/tsconfig.build.json +8 -0
  391. package/tsconfig.json +21 -0
  392. package/tsup.config.ts +60 -0
@@ -0,0 +1,607 @@
1
+ import { afterEach, beforeAll, beforeEach, describe, expect, it, jest } from '@jest/globals';
2
+ import { GeeqSocket, GeeqTrackedTx, createAttestationTx, createCoinTransferTx, defaultWebSocketFactory, generateKeypair, initGeeq, keypairFromMnemonic, socketRoute, toBase64, transactionIdOf, } from '../src/index';
3
+ import { fakeSocketFactory, uutStatus } from './fixtures/fake-socket';
4
+ const MNEMONIC = 'legal winner thank year wave sausage worth useful legal winner thank yellow';
5
+ const URL_WS = 'wss://10.0.0.1:2082/ws/';
6
+ const HASH = new Uint8Array(32).fill(7);
7
+ const ID = toBase64(HASH);
8
+ let keypair;
9
+ let receiver;
10
+ let open = [];
11
+ // A browser WebSocket fits the factory type with no adapter. Compile-time only.
12
+ const browserFactory = (url) => new WebSocket(url);
13
+ void browserFactory;
14
+ beforeAll(async () => {
15
+ await initGeeq();
16
+ keypair = await keypairFromMnemonic(MNEMONIC);
17
+ receiver = await generateKeypair();
18
+ });
19
+ beforeEach(() => {
20
+ jest.useFakeTimers({ doNotFake: ['nextTick', 'queueMicrotask'] });
21
+ });
22
+ afterEach(() => {
23
+ for (const socket of open)
24
+ socket.dispose();
25
+ open = [];
26
+ jest.useRealTimers();
27
+ });
28
+ async function failure(promise) {
29
+ try {
30
+ await promise;
31
+ }
32
+ catch (error) {
33
+ return error;
34
+ }
35
+ throw new Error('expected a rejection');
36
+ }
37
+ /** Connect through a fake socket whose handshake completes at once. */
38
+ async function connected(options = {}) {
39
+ const factory = fakeSocketFactory();
40
+ const pending = GeeqSocket.connect({ url: URL_WS, webSocket: factory, ...options });
41
+ factory.last().open();
42
+ const socket = await pending;
43
+ open.push(socket);
44
+ return { socket, factory, wire: () => factory.last() };
45
+ }
46
+ describe('socketRoute', () => {
47
+ it('is the node origin with a ws scheme and the /ws/ path, trailing slash included', () => {
48
+ expect(socketRoute('https://10.0.0.1:2082')).toBe('wss://10.0.0.1:2082/ws/');
49
+ expect(socketRoute('http://127.0.0.1:3000')).toBe('ws://127.0.0.1:3000/ws/');
50
+ expect(socketRoute('https://node.example.com:2082/')).toBe('wss://node.example.com:2082/ws/');
51
+ });
52
+ });
53
+ describe('GeeqSocket.connect', () => {
54
+ it('derives the socket from the node endpoint, on the node port', async () => {
55
+ const factory = fakeSocketFactory();
56
+ const pending = GeeqSocket.connect({ endpoint: 'https://10.0.0.1', webSocket: factory });
57
+ factory.last().open();
58
+ const socket = await pending;
59
+ open.push(socket);
60
+ expect(factory.last().url).toBe('wss://10.0.0.1:2082/ws/');
61
+ expect(socket.url).toBe('wss://10.0.0.1:2082/ws/');
62
+ expect(socket.state).toBe('open');
63
+ });
64
+ it('wants exactly one of endpoint and url', async () => {
65
+ await expect(GeeqSocket.connect({})).rejects.toMatchObject({ code: 'invalid-argument' });
66
+ await expect(GeeqSocket.connect({ endpoint: 'https://10.0.0.1:2082', url: URL_WS })).rejects.toMatchObject({ code: 'invalid-argument' });
67
+ });
68
+ it('refuses a url that is not a socket', async () => {
69
+ const error = await failure(GeeqSocket.connect({ url: 'https://10.0.0.1:2082' }));
70
+ expect(error.code).toBe('invalid-argument');
71
+ expect(error.message).toMatch(/pass it as endpoint/u);
72
+ });
73
+ // A socket that never opened is a certificate, a page scheme or a wrong URL. Retrying
74
+ // quietly would hide which, so the first failure is reported, with all four causes.
75
+ it('reports a failed first handshake instead of retrying it', async () => {
76
+ const factory = fakeSocketFactory();
77
+ const pending = GeeqSocket.connect({ url: URL_WS, webSocket: factory });
78
+ factory.last().drop();
79
+ const error = await failure(pending);
80
+ expect(error.code).toBe('socket-unreachable');
81
+ expect(error.message).toMatch(/TLS certificate/u);
82
+ expect(error.message).toMatch(/\/ws\//u);
83
+ await jest.advanceTimersByTimeAsync(60_000);
84
+ expect(factory.sockets).toHaveLength(1);
85
+ });
86
+ it('gives up on a handshake that never completes', async () => {
87
+ const factory = fakeSocketFactory();
88
+ const pending = GeeqSocket.connect({ url: URL_WS, webSocket: factory, openTimeoutMs: 500 });
89
+ const caught = failure(pending);
90
+ await jest.advanceTimersByTimeAsync(500);
91
+ const error = await caught;
92
+ expect(error.code).toBe('socket-unreachable');
93
+ expect(error.message).toMatch(/within 500ms/u);
94
+ expect(factory.last().readyState).toBe(3);
95
+ });
96
+ it('can be abandoned with a signal', async () => {
97
+ const controller = new AbortController();
98
+ const factory = fakeSocketFactory();
99
+ const pending = GeeqSocket.connect({ url: URL_WS, webSocket: factory, signal: controller.signal });
100
+ controller.abort(new Error('user left'));
101
+ await expect(pending).rejects.toThrow('user left');
102
+ expect(factory.last().readyState).toBe(3);
103
+ });
104
+ it('names the fix when the runtime has no WebSocket', async () => {
105
+ const real = globalThis.WebSocket;
106
+ Object.assign(globalThis, { WebSocket: undefined });
107
+ try {
108
+ expect(() => defaultWebSocketFactory(URL_WS)).toThrow(expect.objectContaining({ code: 'socket-unreachable' }));
109
+ const error = await failure(GeeqSocket.connect({ url: URL_WS }));
110
+ expect(error.code).toBe('socket-unreachable');
111
+ expect(error.message).toMatch(/import WebSocket from "ws"/u);
112
+ }
113
+ finally {
114
+ Object.assign(globalThis, { WebSocket: real });
115
+ }
116
+ });
117
+ it('refuses a ws:// socket from an https page before trying it', async () => {
118
+ Object.assign(globalThis, { location: { protocol: 'https:' } });
119
+ try {
120
+ const error = await failure(GeeqSocket.connect({ url: 'ws://10.0.0.1:3100/ws/' }));
121
+ expect(error.code).toBe('socket-unreachable');
122
+ expect(error.message).toMatch(/mixed content/u);
123
+ }
124
+ finally {
125
+ delete globalThis.location;
126
+ }
127
+ });
128
+ });
129
+ describe('subscriptions', () => {
130
+ it('subscribes an account under its public key and its account id', async () => {
131
+ const { socket, wire } = await connected();
132
+ await socket.subscribe(keypair);
133
+ expect(wire().frames()).toEqual([
134
+ {
135
+ event: 'client',
136
+ payload: { publicKeys: [toBase64(keypair.publicKey), toBase64(keypair.accountId)] },
137
+ },
138
+ ]);
139
+ });
140
+ // The node replaces a connection's subscription with each frame and has no
141
+ // unsubscribe, so subscribing one more account must resend everyone.
142
+ it('only ever grows the set, and always sends all of it', async () => {
143
+ const { socket, wire } = await connected();
144
+ await socket.subscribe(keypair);
145
+ await socket.subscribe(receiver.accountId);
146
+ const last = wire().frames().at(-1);
147
+ expect(last).toEqual({
148
+ event: 'client',
149
+ payload: {
150
+ publicKeys: [
151
+ toBase64(keypair.publicKey),
152
+ toBase64(keypair.accountId),
153
+ toBase64(receiver.accountId),
154
+ ],
155
+ },
156
+ });
157
+ expect(socket.subscriptions).toHaveLength(3);
158
+ });
159
+ it('does not tell a connection the same thing twice', async () => {
160
+ const { socket, wire } = await connected();
161
+ await socket.subscribe(keypair);
162
+ await socket.subscribe(keypair);
163
+ await socket.subscribe(toBase64(keypair.accountId));
164
+ expect(wire().frames()).toHaveLength(1);
165
+ });
166
+ it('rejects a key that is not 32 bytes, without half-applying the list', async () => {
167
+ const { socket } = await connected();
168
+ const error = await failure(socket.subscribe([keypair, 'AAAA']));
169
+ expect(error.code).toBe('invalid-argument');
170
+ expect(socket.subscriptions).toEqual([]);
171
+ });
172
+ it('sends listen and block-explorer frames', async () => {
173
+ const { socket, wire } = await connected();
174
+ await socket.listen();
175
+ await socket.listen({ blockNumber: 12, now: false });
176
+ await socket.subscribeBlockExplorer();
177
+ expect(wire().frames()).toEqual([
178
+ { event: 'listen', payload: { now: true } },
179
+ { event: 'listen', payload: { blockNumber: 12, now: false } },
180
+ { event: 'block-explorer' },
181
+ ]);
182
+ });
183
+ });
184
+ describe('staying connected', () => {
185
+ it('sends a text ping every 25 seconds while open', async () => {
186
+ const { wire } = await connected();
187
+ await jest.advanceTimersByTimeAsync(25_000 * 3);
188
+ expect(wire().sent.filter((frame) => frame === 'ping')).toHaveLength(3);
189
+ });
190
+ // A node forgets a connection's subscriptions when it drops. Replaying them before
191
+ // announcing 'open' is what lets listeners trust the state they are told about.
192
+ it('reconnects with backoff and restores every subscription before announcing it', async () => {
193
+ const { socket, factory, wire } = await connected();
194
+ await socket.subscribe(keypair);
195
+ await socket.listen();
196
+ const states = [];
197
+ socket.onConnectionChange((state) => {
198
+ states.push(state);
199
+ if (state === 'open')
200
+ expect(wire().frames()).toHaveLength(2);
201
+ });
202
+ wire().drop();
203
+ expect(socket.state).toBe('reconnecting');
204
+ await jest.advanceTimersByTimeAsync(999);
205
+ expect(factory.sockets).toHaveLength(1);
206
+ await jest.advanceTimersByTimeAsync(1);
207
+ expect(factory.sockets).toHaveLength(2);
208
+ // A second failure doubles the delay.
209
+ wire().drop();
210
+ await jest.advanceTimersByTimeAsync(1_999);
211
+ expect(factory.sockets).toHaveLength(2);
212
+ await jest.advanceTimersByTimeAsync(1);
213
+ expect(factory.sockets).toHaveLength(3);
214
+ wire().open();
215
+ expect(states).toEqual(['reconnecting', 'open']);
216
+ expect(wire().frames().map((frame) => frame.event)).toEqual(['client', 'listen']);
217
+ });
218
+ it('ignores a superseded connection', async () => {
219
+ const { socket, factory } = await connected();
220
+ const first = factory.last();
221
+ first.drop();
222
+ await jest.advanceTimersByTimeAsync(1_000);
223
+ factory.last().open();
224
+ const seen = [];
225
+ socket.on('fee-update', (payload) => seen.push(payload));
226
+ first.receive({ event: 'fee-update', payload: 'stale' });
227
+ factory.last().receive({ event: 'fee-update', payload: 'fresh' });
228
+ expect(seen).toEqual(['fresh']);
229
+ });
230
+ it('holds a send until the socket is back', async () => {
231
+ const { socket, wire } = await connected();
232
+ wire().drop();
233
+ const sending = socket.send('custom', { a: 1 });
234
+ await jest.advanceTimersByTimeAsync(1_000);
235
+ wire().open();
236
+ await sending;
237
+ expect(wire().frames()).toEqual([{ event: 'custom', payload: { a: 1 } }]);
238
+ });
239
+ // A browser delivers 'close' asynchronously, so a socket can be open by the SDK's books
240
+ // and already closed by its own. A subscription reported as sent in that window never
241
+ // reached the node, and a transaction submitted on the strength of it is never heard.
242
+ it('does not report a frame as sent on a socket that is already closing', async () => {
243
+ const { socket, factory, wire } = await connected();
244
+ wire().readyState = 3; // closed, with its close event still to come
245
+ let sent = false;
246
+ const subscribing = socket.subscribe(keypair).then(() => {
247
+ sent = true;
248
+ });
249
+ await jest.advanceTimersByTimeAsync(0);
250
+ expect(sent).toBe(false);
251
+ expect(socket.state).toBe('reconnecting');
252
+ await jest.advanceTimersByTimeAsync(1_000);
253
+ expect(factory.sockets).toHaveLength(2);
254
+ factory.last().open();
255
+ await subscribing;
256
+ expect(factory.last().frames().map((frame) => frame.event)).toEqual(['client']);
257
+ });
258
+ // Only a connection that took its subscriptions is a working one. Resetting the backoff
259
+ // on the handshake alone would redial a node that keeps dying there every second.
260
+ it('does not announce a connection that dies while its subscriptions are replayed', async () => {
261
+ const { socket, factory, wire } = await connected();
262
+ await socket.subscribe(keypair);
263
+ const states = [];
264
+ socket.onConnectionChange((state) => states.push(state));
265
+ wire().drop();
266
+ let sent = false;
267
+ const sending = socket.send('custom', { a: 1 }).then(() => {
268
+ sent = true;
269
+ });
270
+ // If an expectation below fails, disposal rejects this mid-wait; report the expectation.
271
+ sending.catch(() => { });
272
+ await jest.advanceTimersByTimeAsync(1_000);
273
+ expect(factory.sockets).toHaveLength(2);
274
+ const dying = factory.last();
275
+ dying.send = () => {
276
+ dying.readyState = 3; // closed under the first replayed frame
277
+ throw new Error('InvalidStateError: the socket is not open');
278
+ };
279
+ dying.open();
280
+ await jest.advanceTimersByTimeAsync(0);
281
+ expect(socket.state).toBe('reconnecting');
282
+ expect(sent).toBe(false);
283
+ // Still the second attempt as far as the backoff is concerned: two seconds, not one.
284
+ await jest.advanceTimersByTimeAsync(1_999);
285
+ expect(factory.sockets).toHaveLength(2);
286
+ await jest.advanceTimersByTimeAsync(1);
287
+ expect(factory.sockets).toHaveLength(3);
288
+ factory.last().open();
289
+ await sending;
290
+ expect(states).toEqual(['reconnecting', 'open']);
291
+ expect(factory.last().frames().map((frame) => frame.event)).toEqual(['client', 'custom']);
292
+ });
293
+ it('rejects a send that waited too long, but keeps a replayed frame', async () => {
294
+ const { socket, factory, wire } = await connected({ openTimeoutMs: 5_000 });
295
+ wire().drop();
296
+ const caught = failure(socket.subscribe(keypair));
297
+ await jest.advanceTimersByTimeAsync(5_000);
298
+ expect((await caught).code).toBe('socket-unreachable');
299
+ // The retry dialled at one second is still mid-handshake.
300
+ expect(factory.sockets).toHaveLength(2);
301
+ factory.last().open();
302
+ expect(factory.last().frames().map((frame) => frame.event)).toEqual(['client']);
303
+ });
304
+ it('stays down when reconnect is off', async () => {
305
+ const { socket, factory, wire } = await connected({ reconnect: false });
306
+ wire().drop();
307
+ await jest.advanceTimersByTimeAsync(60_000);
308
+ expect(socket.state).toBe('closed');
309
+ expect(factory.sockets).toHaveLength(1);
310
+ });
311
+ });
312
+ describe('receiving', () => {
313
+ it('hands any event to its listeners, and every frame to onMessage', async () => {
314
+ const { socket, wire } = await connected();
315
+ const updates = [];
316
+ const all = [];
317
+ const off = socket.on('coin-account-update', (payload) => updates.push(payload));
318
+ socket.onMessage((message) => all.push(message.event));
319
+ wire().receive({ event: 'coin-account-update', payload: { buffer: 'AA==', block: 3 } });
320
+ wire().receive({ event: 'something-new', payload: 1 });
321
+ off();
322
+ wire().receive({ event: 'coin-account-update', payload: { buffer: 'AA==', block: 4 } });
323
+ expect(updates).toEqual([{ buffer: 'AA==', block: 3 }]);
324
+ expect(all).toEqual(['coin-account-update', 'something-new', 'coin-account-update']);
325
+ });
326
+ it('ignores heartbeat replies and anything else that is not an event', async () => {
327
+ const { socket, wire } = await connected();
328
+ const all = [];
329
+ socket.onMessage((message) => all.push(message));
330
+ wire().receive('pong');
331
+ wire().receive('[1,2]');
332
+ wire().receive({ payload: 'no event name' });
333
+ expect(all).toEqual([]);
334
+ });
335
+ it('keeps delivering when a listener throws', async () => {
336
+ const { socket, wire } = await connected();
337
+ const report = jest.fn();
338
+ Object.assign(globalThis, { reportError: report });
339
+ try {
340
+ const seen = [];
341
+ socket.on('x', () => {
342
+ throw new Error('app bug');
343
+ });
344
+ socket.on('x', (payload) => seen.push(payload));
345
+ wire().receive({ event: 'x', payload: 1 });
346
+ expect(seen).toEqual([1]);
347
+ expect(report).toHaveBeenCalledWith(expect.objectContaining({ message: 'app bug' }));
348
+ }
349
+ finally {
350
+ delete globalThis.reportError;
351
+ }
352
+ });
353
+ it('parses transaction statuses the way nodes actually send them', async () => {
354
+ const { socket, wire } = await connected();
355
+ const seen = [];
356
+ socket.onTxStatus((event) => seen.push(event));
357
+ wire().receive(uutStatus(ID, 'invalid', { error: 'Nonce Check' }));
358
+ // An older node: numeric status, one publicKey, no type or createdAt.
359
+ wire().receive({
360
+ event: 'uut_status',
361
+ payload: { refHash: ID, publicKey: 'cGs=', status: 3, isValid: true, blockNumber: 9 },
362
+ });
363
+ wire().receive({ event: 'uut_status', payload: { refHash: ID, status: 'mystery' } });
364
+ expect(seen).toHaveLength(2);
365
+ expect(seen[0]).toMatchObject({
366
+ id: ID,
367
+ verdict: 'invalid',
368
+ isValid: false,
369
+ error: 'Nonce Check',
370
+ blockNumber: 42,
371
+ vlbBlock: 43,
372
+ direction: 'outgoing',
373
+ });
374
+ expect(seen[0]?.createdAt?.toISOString()).toBe('2026-09-30T10:00:00.000Z');
375
+ expect(seen[1]).toMatchObject({
376
+ verdict: 'valid',
377
+ isValid: true,
378
+ signer: 'cGs=',
379
+ direction: undefined,
380
+ createdAt: undefined,
381
+ vlbBlock: undefined,
382
+ });
383
+ });
384
+ });
385
+ describe('socket.track', () => {
386
+ const tx = () => ({ transactionHash: HASH, publicKey: keypair.publicKey });
387
+ // Subscribing after submitting is a race the status can win.
388
+ it('subscribes the signer before it resolves', async () => {
389
+ const { socket, wire } = await connected();
390
+ const tracked = await socket.track(tx());
391
+ expect(tracked).toBeInstanceOf(GeeqTrackedTx);
392
+ expect(tracked.id).toBe(ID);
393
+ expect(tracked.status).toBe('pending');
394
+ expect(wire().frames()).toEqual([
395
+ {
396
+ event: 'client',
397
+ payload: { publicKeys: [toBase64(keypair.publicKey), toBase64(keypair.accountId)] },
398
+ },
399
+ ]);
400
+ });
401
+ it('settles confirmed on valid, and ignores everything after', async () => {
402
+ const { socket, wire } = await connected();
403
+ const tracked = await socket.track(tx());
404
+ const changes = [];
405
+ tracked.onChange((t) => changes.push(t.status));
406
+ wire().receive(uutStatus(toBase64(new Uint8Array(32).fill(1)), 'invalid'));
407
+ wire().receive(uutStatus(ID, 'valid'));
408
+ wire().receive(uutStatus(ID, 'invalid'));
409
+ const outcome = await tracked.settled();
410
+ expect(outcome).toMatchObject({ status: 'confirmed', id: ID, event: { verdict: 'valid' } });
411
+ expect(tracked.events).toHaveLength(1);
412
+ expect(changes).toEqual(['confirmed']);
413
+ expect(socket.toJSON()).toMatchObject({ tracking: 0 });
414
+ });
415
+ it('settles rejected on invalid, with the node reason', async () => {
416
+ const { socket, wire } = await connected();
417
+ const tracked = await socket.track(tx());
418
+ wire().receive(uutStatus(ID, 'invalid', { error: 'Nonce Check failed' }));
419
+ expect(await tracked.settled()).toMatchObject({
420
+ status: 'rejected',
421
+ reason: 'invalid',
422
+ message: 'Nonce Check failed',
423
+ });
424
+ });
425
+ it('settles rejected on incorrectlyIncluded', async () => {
426
+ const { socket, wire } = await connected();
427
+ const tracked = await socket.track(tx());
428
+ wire().receive(uutStatus(ID, 'incorrectlyIncluded'));
429
+ expect(await tracked.settled()).toMatchObject({ reason: 'incorrectly-included' });
430
+ });
431
+ // A node files a transfer's status under the receiver's account too, marked incoming. A
432
+ // page subscribed to both ends hears it twice and must decide it once.
433
+ it('hears the receiver copy as incoming, and settles once on both copies', async () => {
434
+ const { socket, wire } = await connected();
435
+ const seen = [];
436
+ socket.onTxStatus((event) => seen.push(event));
437
+ await socket.subscribe(receiver);
438
+ const tracked = await socket.track(tx());
439
+ const changes = [];
440
+ tracked.onChange((t) => changes.push(t.status));
441
+ wire().receive(uutStatus(ID, 'valid'));
442
+ wire().receive(uutStatus(ID, 'valid', { type: 'incoming' }));
443
+ expect(seen.map((event) => event.direction)).toEqual(['outgoing', 'incoming']);
444
+ expect(changes).toEqual(['confirmed']);
445
+ expect(tracked.events).toHaveLength(1);
446
+ expect(await tracked.settled()).toMatchObject({ status: 'confirmed' });
447
+ });
448
+ // First-event-wins reports false failures: a restarted node can carry a nonsane
449
+ // transaction forward into a block that then decides it.
450
+ it('lets a real verdict overturn a nonsane within the grace window', async () => {
451
+ const { socket, wire } = await connected();
452
+ const tracked = await socket.track(tx());
453
+ wire().receive(uutStatus(ID, 'nonsane'));
454
+ expect(tracked.status).toBe('pending');
455
+ expect(tracked.events.map((event) => event.verdict)).toEqual(['nonsane']);
456
+ await jest.advanceTimersByTimeAsync(10_000);
457
+ wire().receive(uutStatus(ID, 'valid'));
458
+ expect(await tracked.settled()).toMatchObject({ status: 'confirmed' });
459
+ });
460
+ it('settles a nonsane that nothing overturns', async () => {
461
+ const { socket, wire } = await connected();
462
+ const tracked = await socket.track(tx(), { nonsaneGraceMs: 2_000 });
463
+ wire().receive(uutStatus(ID, 'nonsane', { error: 'Receive uvt, reason: target block' }));
464
+ await jest.advanceTimersByTimeAsync(2_000);
465
+ expect(await tracked.settled()).toMatchObject({
466
+ status: 'rejected',
467
+ reason: 'nonsane',
468
+ message: 'Receive uvt, reason: target block',
469
+ });
470
+ });
471
+ // A node emits nothing for a submission it answered with an error, so waiting on the
472
+ // socket would turn a clear refusal into a timeout.
473
+ it('settles at once when the caller reports a refused submission', async () => {
474
+ const { socket } = await connected();
475
+ const tracked = await socket.track(tx());
476
+ tracked.refused({ status: 503, message: 'Block chain is stopped' });
477
+ expect(await tracked.settled()).toMatchObject({
478
+ status: 'rejected',
479
+ reason: 'refused',
480
+ httpStatus: 503,
481
+ message: 'Block chain is stopped',
482
+ });
483
+ });
484
+ it('uses the nonsane reason for a refusal the node also explained', async () => {
485
+ const { socket, wire } = await connected();
486
+ const tracked = await socket.track(tx());
487
+ wire().receive(uutStatus(ID, 'nonsane', { error: 'Receive uat, reason: nonce' }));
488
+ tracked.refused({ status: 400 });
489
+ expect(await tracked.settled()).toMatchObject({
490
+ reason: 'refused',
491
+ httpStatus: 400,
492
+ message: 'Receive uat, reason: nonce',
493
+ event: { verdict: 'nonsane' },
494
+ });
495
+ });
496
+ it('says unknown, not rejected, when nothing final arrives in time', async () => {
497
+ const { socket } = await connected();
498
+ const tracked = await socket.track(tx(), { timeoutMs: 60_000 });
499
+ await jest.advanceTimersByTimeAsync(60_000);
500
+ const outcome = await tracked.settled();
501
+ expect(outcome).toMatchObject({ status: 'unknown', reason: 'timeout' });
502
+ expect(outcome.status === 'unknown' && outcome.message).toMatch(/not a rejection/u);
503
+ });
504
+ it('returns the same handle for the same transaction', async () => {
505
+ const { socket } = await connected();
506
+ const a = await socket.track(tx());
507
+ const b = await socket.track(tx());
508
+ expect(b).toBe(a);
509
+ });
510
+ it('stops tracking on release', async () => {
511
+ const { socket, wire } = await connected();
512
+ const tracked = await socket.track(tx());
513
+ tracked.release();
514
+ wire().receive(uutStatus(ID, 'valid'));
515
+ expect(await tracked.settled()).toMatchObject({ status: 'unknown', reason: 'released' });
516
+ });
517
+ it('needs someone to subscribe to', async () => {
518
+ const { socket } = await connected();
519
+ await expect(socket.track({ transactionHash: HASH })).rejects.toMatchObject({
520
+ code: 'invalid-argument',
521
+ });
522
+ await expect(socket.track({ transactionHash: new Uint8Array(3) })).rejects.toMatchObject({
523
+ code: 'invalid-argument',
524
+ });
525
+ });
526
+ it('does not hand back a handle it could not subscribe for', async () => {
527
+ const { socket, wire } = await connected({ openTimeoutMs: 1_000 });
528
+ wire().drop();
529
+ const caught = failure(socket.track(tx()));
530
+ await jest.advanceTimersByTimeAsync(1_000);
531
+ expect((await caught).code).toBe('socket-unreachable');
532
+ expect(socket.toJSON()).toMatchObject({ tracking: 0 });
533
+ });
534
+ it('matches the transactions the SDK builds, by the hash each kind is known by', async () => {
535
+ const { socket, wire } = await connected();
536
+ const chainState = { chainNumber: 1, blockNumber: 42, nodeNumber: 3, nonce: 7 };
537
+ const transfer = await createCoinTransferTx({
538
+ keypair,
539
+ to: receiver.accountId,
540
+ amount: 150n,
541
+ chainState,
542
+ });
543
+ const attestation = await createAttestationTx({
544
+ keypair,
545
+ attestHash: new Uint8Array(32).fill(9),
546
+ chainState,
547
+ });
548
+ const a = await socket.track(transfer);
549
+ const b = await socket.track(attestation);
550
+ expect(a.id).toBe(transactionIdOf(transfer));
551
+ expect(b.id).toBe(transactionIdOf(attestation));
552
+ wire().receive(uutStatus(transactionIdOf(transfer), 'valid'));
553
+ wire().receive(uutStatus(transactionIdOf(attestation), 'invalid'));
554
+ expect((await a.settled()).status).toBe('confirmed');
555
+ expect((await b.settled()).status).toBe('rejected');
556
+ });
557
+ });
558
+ describe('dispose', () => {
559
+ it('settles everything pending as unknown and refuses further use', async () => {
560
+ const { socket, wire } = await connected();
561
+ const tracked = await socket.track({ transactionHash: HASH, publicKey: keypair.publicKey });
562
+ const states = [];
563
+ socket.onConnectionChange((state) => states.push(state));
564
+ socket.dispose();
565
+ expect(await tracked.settled()).toMatchObject({ status: 'unknown', reason: 'closed' });
566
+ expect(states).toEqual(['closed']);
567
+ expect(wire().readyState).toBe(3);
568
+ await expect(socket.subscribe(keypair)).rejects.toMatchObject({ code: 'socket-closed' });
569
+ await expect(socket.send('x')).rejects.toMatchObject({ code: 'socket-closed' });
570
+ });
571
+ it('rejects a send still waiting for the connection', async () => {
572
+ const { socket, wire } = await connected();
573
+ wire().drop();
574
+ const sending = socket.send('x');
575
+ socket.dispose();
576
+ await expect(sending).rejects.toMatchObject({ code: 'socket-closed' });
577
+ });
578
+ it('stops the heartbeat and the reconnect loop', async () => {
579
+ const { socket, factory, wire } = await connected();
580
+ wire().drop();
581
+ socket.dispose();
582
+ await jest.advanceTimersByTimeAsync(120_000);
583
+ expect(factory.sockets).toHaveLength(1);
584
+ });
585
+ });
586
+ describe('in a reactive framework', () => {
587
+ // Nothing but the URL is enumerable, so a spread, a devtools panel or a structured
588
+ // clone never walks into a native socket, a timer or a listener.
589
+ it('exposes only its URL as own enumerable state', async () => {
590
+ const { socket } = await connected();
591
+ expect(Object.keys(socket)).toEqual(['url']);
592
+ expect(socket.toJSON()).toEqual({
593
+ url: URL_WS,
594
+ state: 'open',
595
+ subscriptions: 0,
596
+ tracking: 0,
597
+ });
598
+ expect(String(socket)).toBe(`GeeqSocket(${URL_WS}, open)`);
599
+ });
600
+ it('works through a Proxy, as ref() would wrap it', async () => {
601
+ const { socket, wire } = await connected();
602
+ const proxied = new Proxy(socket, {});
603
+ await proxied.subscribe(keypair);
604
+ expect(wire().frames()).toHaveLength(1);
605
+ });
606
+ });
607
+ //# sourceMappingURL=socket.test.js.map