mppx 0.6.31 → 0.7.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 (477) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/Challenge.d.ts.map +1 -1
  3. package/dist/Challenge.js +9 -7
  4. package/dist/Challenge.js.map +1 -1
  5. package/dist/Constants.d.ts +46 -0
  6. package/dist/Constants.d.ts.map +1 -0
  7. package/dist/Constants.js +46 -0
  8. package/dist/Constants.js.map +1 -0
  9. package/dist/Credential.d.ts.map +1 -1
  10. package/dist/Credential.js +5 -4
  11. package/dist/Credential.js.map +1 -1
  12. package/dist/Method.d.ts +32 -4
  13. package/dist/Method.d.ts.map +1 -1
  14. package/dist/Method.js +5 -2
  15. package/dist/Method.js.map +1 -1
  16. package/dist/Receipt.d.ts.map +1 -1
  17. package/dist/Receipt.js +3 -2
  18. package/dist/Receipt.js.map +1 -1
  19. package/dist/cli/cli.d.ts.map +1 -1
  20. package/dist/cli/cli.js +19 -11
  21. package/dist/cli/cli.js.map +1 -1
  22. package/dist/cli/plugins/tempo.d.ts.map +1 -1
  23. package/dist/cli/plugins/tempo.js +17 -6
  24. package/dist/cli/plugins/tempo.js.map +1 -1
  25. package/dist/cli/utils.d.ts +5 -0
  26. package/dist/cli/utils.d.ts.map +1 -1
  27. package/dist/cli/utils.js +10 -0
  28. package/dist/cli/utils.js.map +1 -1
  29. package/dist/client/Methods.d.ts +5 -2
  30. package/dist/client/Methods.d.ts.map +1 -1
  31. package/dist/client/Methods.js +5 -2
  32. package/dist/client/Methods.js.map +1 -1
  33. package/dist/client/Transport.d.ts.map +1 -1
  34. package/dist/client/Transport.js +4 -5
  35. package/dist/client/Transport.js.map +1 -1
  36. package/dist/client/index.d.ts +2 -1
  37. package/dist/client/index.d.ts.map +1 -1
  38. package/dist/client/index.js +2 -1
  39. package/dist/client/index.js.map +1 -1
  40. package/dist/client/internal/Fetch.d.ts.map +1 -1
  41. package/dist/client/internal/Fetch.js +14 -6
  42. package/dist/client/internal/Fetch.js.map +1 -1
  43. package/dist/evm/server/Methods.d.ts +1 -1
  44. package/dist/evm/server/Methods.d.ts.map +1 -1
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +1 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/internal/AcceptPayment.d.ts +3 -0
  50. package/dist/internal/AcceptPayment.d.ts.map +1 -1
  51. package/dist/internal/AcceptPayment.js +15 -11
  52. package/dist/internal/AcceptPayment.js.map +1 -1
  53. package/dist/mcp-sdk/client/McpClient.d.ts +12 -5
  54. package/dist/mcp-sdk/client/McpClient.d.ts.map +1 -1
  55. package/dist/mcp-sdk/client/McpClient.js +55 -42
  56. package/dist/mcp-sdk/client/McpClient.js.map +1 -1
  57. package/dist/server/Mppx.d.ts +11 -3
  58. package/dist/server/Mppx.d.ts.map +1 -1
  59. package/dist/server/Mppx.js +76 -27
  60. package/dist/server/Mppx.js.map +1 -1
  61. package/dist/server/Response.d.ts.map +1 -1
  62. package/dist/server/Response.js +2 -1
  63. package/dist/server/Response.js.map +1 -1
  64. package/dist/server/Transport.d.ts.map +1 -1
  65. package/dist/server/Transport.js +4 -3
  66. package/dist/server/Transport.js.map +1 -1
  67. package/dist/server/index.d.ts +1 -0
  68. package/dist/server/index.d.ts.map +1 -1
  69. package/dist/server/index.js +1 -0
  70. package/dist/server/index.js.map +1 -1
  71. package/dist/stripe/client/Charge.d.ts +1 -1
  72. package/dist/stripe/client/Charge.d.ts.map +1 -1
  73. package/dist/stripe/client/Charge.js +3 -1
  74. package/dist/stripe/client/Charge.js.map +1 -1
  75. package/dist/stripe/server/Charge.d.ts +1 -1
  76. package/dist/stripe/server/Charge.d.ts.map +1 -1
  77. package/dist/stripe/server/Charge.js +9 -2
  78. package/dist/stripe/server/Charge.js.map +1 -1
  79. package/dist/stripe/server/Methods.d.ts +1 -1
  80. package/dist/stripe/server/Methods.d.ts.map +1 -1
  81. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  82. package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
  83. package/dist/stripe/server/internal/html.gen.js +1 -1
  84. package/dist/stripe/server/internal/html.gen.js.map +1 -1
  85. package/dist/tempo/Methods.d.ts +18 -0
  86. package/dist/tempo/Methods.d.ts.map +1 -1
  87. package/dist/tempo/Methods.js +16 -1
  88. package/dist/tempo/Methods.js.map +1 -1
  89. package/dist/tempo/client/Charge.d.ts +6 -0
  90. package/dist/tempo/client/Charge.d.ts.map +1 -1
  91. package/dist/tempo/client/Charge.js +9 -2
  92. package/dist/tempo/client/Charge.js.map +1 -1
  93. package/dist/tempo/client/Methods.d.ts +36 -7
  94. package/dist/tempo/client/Methods.d.ts.map +1 -1
  95. package/dist/tempo/client/Methods.js +12 -5
  96. package/dist/tempo/client/Methods.js.map +1 -1
  97. package/dist/tempo/client/index.d.ts +7 -4
  98. package/dist/tempo/client/index.d.ts.map +1 -1
  99. package/dist/tempo/client/index.js +5 -3
  100. package/dist/tempo/client/index.js.map +1 -1
  101. package/dist/tempo/index.d.ts +1 -0
  102. package/dist/tempo/index.d.ts.map +1 -1
  103. package/dist/tempo/index.js +1 -0
  104. package/dist/tempo/index.js.map +1 -1
  105. package/dist/tempo/internal/fee-payer.d.ts +21 -1
  106. package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
  107. package/dist/tempo/internal/fee-payer.js +109 -4
  108. package/dist/tempo/internal/fee-payer.js.map +1 -1
  109. package/dist/tempo/{client → legacy/client}/ChannelOps.d.ts +19 -6
  110. package/dist/tempo/legacy/client/ChannelOps.d.ts.map +1 -0
  111. package/dist/tempo/{client → legacy/client}/ChannelOps.js +9 -3
  112. package/dist/tempo/legacy/client/ChannelOps.js.map +1 -0
  113. package/dist/tempo/{client → legacy/client}/Session.d.ts +23 -4
  114. package/dist/tempo/legacy/client/Session.d.ts.map +1 -0
  115. package/dist/tempo/{client → legacy/client}/Session.js +14 -7
  116. package/dist/tempo/legacy/client/Session.js.map +1 -0
  117. package/dist/tempo/{client → legacy/client}/SessionManager.d.ts +20 -5
  118. package/dist/tempo/legacy/client/SessionManager.d.ts.map +1 -0
  119. package/dist/tempo/{client → legacy/client}/SessionManager.js +20 -16
  120. package/dist/tempo/legacy/client/SessionManager.js.map +1 -0
  121. package/dist/tempo/legacy/client/index.d.ts +7 -0
  122. package/dist/tempo/legacy/client/index.d.ts.map +1 -0
  123. package/dist/tempo/legacy/client/index.js +5 -0
  124. package/dist/tempo/legacy/client/index.js.map +1 -0
  125. package/dist/tempo/legacy/index.d.ts +7 -0
  126. package/dist/tempo/legacy/index.d.ts.map +1 -0
  127. package/dist/tempo/legacy/index.js +7 -0
  128. package/dist/tempo/legacy/index.js.map +1 -0
  129. package/dist/tempo/{server → legacy/server}/Session.d.ts +28 -11
  130. package/dist/tempo/legacy/server/Session.d.ts.map +1 -0
  131. package/dist/tempo/{server → legacy/server}/Session.js +12 -10
  132. package/dist/tempo/legacy/server/Session.js.map +1 -0
  133. package/dist/tempo/legacy/server/index.d.ts +5 -0
  134. package/dist/tempo/legacy/server/index.d.ts.map +1 -0
  135. package/dist/tempo/legacy/server/index.js +5 -0
  136. package/dist/tempo/legacy/server/index.js.map +1 -0
  137. package/dist/tempo/{session → legacy/session}/Chain.d.ts +30 -23
  138. package/dist/tempo/legacy/session/Chain.d.ts.map +1 -0
  139. package/dist/tempo/{session → legacy/session}/Chain.js +12 -11
  140. package/dist/tempo/legacy/session/Chain.js.map +1 -0
  141. package/dist/tempo/{session → legacy/session}/Channel.d.ts +1 -0
  142. package/dist/tempo/legacy/session/Channel.d.ts.map +1 -0
  143. package/dist/tempo/legacy/session/Channel.js.map +1 -0
  144. package/dist/tempo/legacy/session/ChannelStore.d.ts +22 -0
  145. package/dist/tempo/legacy/session/ChannelStore.d.ts.map +1 -0
  146. package/dist/tempo/legacy/session/ChannelStore.js +6 -0
  147. package/dist/tempo/legacy/session/ChannelStore.js.map +1 -0
  148. package/dist/tempo/legacy/session/Types.d.ts +73 -0
  149. package/dist/tempo/legacy/session/Types.d.ts.map +1 -0
  150. package/dist/tempo/legacy/session/Types.js.map +1 -0
  151. package/dist/tempo/{session → legacy/session}/Voucher.d.ts +4 -4
  152. package/dist/tempo/legacy/session/Voucher.d.ts.map +1 -0
  153. package/dist/tempo/{session → legacy/session}/Voucher.js +1 -1
  154. package/dist/tempo/legacy/session/Voucher.js.map +1 -0
  155. package/dist/tempo/{session → legacy/session}/escrow.abi.d.ts +1 -0
  156. package/dist/tempo/{session → legacy/session}/escrow.abi.d.ts.map +1 -1
  157. package/dist/tempo/{session → legacy/session}/escrow.abi.js +1 -0
  158. package/dist/tempo/legacy/session/escrow.abi.js.map +1 -0
  159. package/dist/tempo/legacy/session/index.d.ts +9 -0
  160. package/dist/tempo/legacy/session/index.d.ts.map +1 -0
  161. package/dist/tempo/legacy/session/index.js +9 -0
  162. package/dist/tempo/legacy/session/index.js.map +1 -0
  163. package/dist/tempo/server/Charge.d.ts +1 -1
  164. package/dist/tempo/server/Charge.d.ts.map +1 -1
  165. package/dist/tempo/server/Charge.js +13 -16
  166. package/dist/tempo/server/Charge.js.map +1 -1
  167. package/dist/tempo/server/Methods.d.ts +63 -6
  168. package/dist/tempo/server/Methods.d.ts.map +1 -1
  169. package/dist/tempo/server/Methods.js +36 -8
  170. package/dist/tempo/server/Methods.js.map +1 -1
  171. package/dist/tempo/server/Subscription.d.ts +1 -1
  172. package/dist/tempo/server/Subscription.d.ts.map +1 -1
  173. package/dist/tempo/server/index.d.ts +6 -5
  174. package/dist/tempo/server/index.d.ts.map +1 -1
  175. package/dist/tempo/server/index.js +5 -5
  176. package/dist/tempo/server/index.js.map +1 -1
  177. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  178. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
  179. package/dist/tempo/server/internal/html.gen.js +1 -1
  180. package/dist/tempo/server/internal/html.gen.js.map +1 -1
  181. package/dist/tempo/server/internal/request-body.d.ts +7 -2
  182. package/dist/tempo/server/internal/request-body.d.ts.map +1 -1
  183. package/dist/tempo/server/internal/request-body.js +20 -3
  184. package/dist/tempo/server/internal/request-body.js.map +1 -1
  185. package/dist/tempo/server/internal/transport.d.ts +8 -4
  186. package/dist/tempo/server/internal/transport.d.ts.map +1 -1
  187. package/dist/tempo/server/internal/transport.js +8 -7
  188. package/dist/tempo/server/internal/transport.js.map +1 -1
  189. package/dist/tempo/session/Snapshot.d.ts +32 -0
  190. package/dist/tempo/session/Snapshot.d.ts.map +1 -0
  191. package/dist/tempo/session/Snapshot.js +37 -0
  192. package/dist/tempo/session/Snapshot.js.map +1 -0
  193. package/dist/tempo/session/client/ChannelOps.d.ts +82 -0
  194. package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -0
  195. package/dist/tempo/session/client/ChannelOps.js +204 -0
  196. package/dist/tempo/session/client/ChannelOps.js.map +1 -0
  197. package/dist/tempo/session/client/CredentialState.d.ts +262 -0
  198. package/dist/tempo/session/client/CredentialState.d.ts.map +1 -0
  199. package/dist/tempo/session/client/CredentialState.js +417 -0
  200. package/dist/tempo/session/client/CredentialState.js.map +1 -0
  201. package/dist/tempo/session/client/ReceiptCoordinator.d.ts +26 -0
  202. package/dist/tempo/session/client/ReceiptCoordinator.d.ts.map +1 -0
  203. package/dist/tempo/session/client/ReceiptCoordinator.js +61 -0
  204. package/dist/tempo/session/client/ReceiptCoordinator.js.map +1 -0
  205. package/dist/tempo/session/client/Runtime.d.ts +464 -0
  206. package/dist/tempo/session/client/Runtime.d.ts.map +1 -0
  207. package/dist/tempo/session/client/Runtime.js +499 -0
  208. package/dist/tempo/session/client/Runtime.js.map +1 -0
  209. package/dist/tempo/session/client/Session.d.ts +132 -0
  210. package/dist/tempo/session/client/Session.d.ts.map +1 -0
  211. package/dist/tempo/session/client/Session.js +55 -0
  212. package/dist/tempo/session/client/Session.js.map +1 -0
  213. package/dist/tempo/session/client/SessionManager.d.ts +120 -0
  214. package/dist/tempo/session/client/SessionManager.d.ts.map +1 -0
  215. package/dist/tempo/session/client/SessionManager.js +627 -0
  216. package/dist/tempo/session/client/SessionManager.js.map +1 -0
  217. package/dist/tempo/session/client/Transports.d.ts +449 -0
  218. package/dist/tempo/session/client/Transports.d.ts.map +1 -0
  219. package/dist/tempo/session/client/Transports.js +721 -0
  220. package/dist/tempo/session/client/Transports.js.map +1 -0
  221. package/dist/tempo/session/client/index.d.ts +12 -0
  222. package/dist/tempo/session/client/index.d.ts.map +1 -0
  223. package/dist/tempo/session/client/index.js +5 -0
  224. package/dist/tempo/session/client/index.js.map +1 -0
  225. package/dist/tempo/session/index.d.ts +7 -8
  226. package/dist/tempo/session/index.d.ts.map +1 -1
  227. package/dist/tempo/session/index.js +7 -8
  228. package/dist/tempo/session/index.js.map +1 -1
  229. package/dist/tempo/session/precompile/Chain.d.ts +319 -0
  230. package/dist/tempo/session/precompile/Chain.d.ts.map +1 -0
  231. package/dist/tempo/session/precompile/Chain.js +492 -0
  232. package/dist/tempo/session/precompile/Chain.js.map +1 -0
  233. package/dist/tempo/session/precompile/Channel.d.ts +46 -0
  234. package/dist/tempo/session/precompile/Channel.d.ts.map +1 -0
  235. package/dist/tempo/session/precompile/Channel.js +56 -0
  236. package/dist/tempo/session/precompile/Channel.js.map +1 -0
  237. package/dist/tempo/session/precompile/Protocol.d.ts +308 -0
  238. package/dist/tempo/session/precompile/Protocol.d.ts.map +1 -0
  239. package/dist/tempo/session/precompile/Protocol.js +264 -0
  240. package/dist/tempo/session/precompile/Protocol.js.map +1 -0
  241. package/dist/tempo/session/precompile/Voucher.d.ts +40 -0
  242. package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -0
  243. package/dist/tempo/session/precompile/Voucher.js +126 -0
  244. package/dist/tempo/session/precompile/Voucher.js.map +1 -0
  245. package/dist/tempo/session/precompile/escrow.abi.d.ts +522 -0
  246. package/dist/tempo/session/precompile/escrow.abi.d.ts.map +1 -0
  247. package/dist/tempo/session/precompile/escrow.abi.js +224 -0
  248. package/dist/tempo/session/precompile/escrow.abi.js.map +1 -0
  249. package/dist/tempo/session/precompile/index.d.ts +24 -0
  250. package/dist/tempo/session/precompile/index.d.ts.map +1 -0
  251. package/dist/tempo/session/precompile/index.js +22 -0
  252. package/dist/tempo/session/precompile/index.js.map +1 -0
  253. package/dist/tempo/session/server/ChannelOps.d.ts +56 -0
  254. package/dist/tempo/session/server/ChannelOps.d.ts.map +1 -0
  255. package/dist/tempo/session/server/ChannelOps.js +91 -0
  256. package/dist/tempo/session/server/ChannelOps.js.map +1 -0
  257. package/dist/tempo/session/server/ChannelStore.d.ts +347 -0
  258. package/dist/tempo/session/server/ChannelStore.d.ts.map +1 -0
  259. package/dist/tempo/session/server/ChannelStore.js +404 -0
  260. package/dist/tempo/session/server/ChannelStore.js.map +1 -0
  261. package/dist/tempo/session/server/CredentialVerification.d.ts +85 -0
  262. package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -0
  263. package/dist/tempo/session/server/CredentialVerification.js +494 -0
  264. package/dist/tempo/session/server/CredentialVerification.js.map +1 -0
  265. package/dist/tempo/session/server/MeteredStream.d.ts +40 -0
  266. package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -0
  267. package/dist/tempo/session/server/MeteredStream.js +42 -0
  268. package/dist/tempo/session/server/MeteredStream.js.map +1 -0
  269. package/dist/tempo/session/server/RequestState.d.ts +208 -0
  270. package/dist/tempo/session/server/RequestState.d.ts.map +1 -0
  271. package/dist/tempo/session/server/RequestState.js +252 -0
  272. package/dist/tempo/session/server/RequestState.js.map +1 -0
  273. package/dist/tempo/session/server/Session.d.ts +169 -0
  274. package/dist/tempo/session/server/Session.d.ts.map +1 -0
  275. package/dist/tempo/session/server/Session.js +351 -0
  276. package/dist/tempo/session/server/Session.js.map +1 -0
  277. package/dist/tempo/session/server/Settlement.d.ts +185 -0
  278. package/dist/tempo/session/server/Settlement.d.ts.map +1 -0
  279. package/dist/tempo/session/server/Settlement.js +250 -0
  280. package/dist/tempo/session/server/Settlement.js.map +1 -0
  281. package/dist/tempo/session/{Sse.d.ts → server/Sse.d.ts} +9 -56
  282. package/dist/tempo/session/server/Sse.d.ts.map +1 -0
  283. package/dist/tempo/session/server/Sse.js +184 -0
  284. package/dist/tempo/session/server/Sse.js.map +1 -0
  285. package/dist/tempo/session/server/Transports.d.ts +89 -0
  286. package/dist/tempo/session/server/Transports.d.ts.map +1 -0
  287. package/dist/tempo/session/server/Transports.js +149 -0
  288. package/dist/tempo/session/server/Transports.js.map +1 -0
  289. package/dist/tempo/session/server/Ws.d.ts +48 -0
  290. package/dist/tempo/session/server/Ws.d.ts.map +1 -0
  291. package/dist/tempo/session/server/Ws.js +244 -0
  292. package/dist/tempo/session/server/Ws.js.map +1 -0
  293. package/dist/tempo/session/server/index.d.ts +4 -0
  294. package/dist/tempo/session/server/index.d.ts.map +1 -0
  295. package/dist/tempo/session/server/index.js +2 -0
  296. package/dist/tempo/session/server/index.js.map +1 -0
  297. package/package.json +6 -1
  298. package/src/Challenge.ts +9 -7
  299. package/src/Constants.ts +58 -0
  300. package/src/Credential.ts +5 -4
  301. package/src/Method.ts +46 -5
  302. package/src/Receipt.ts +3 -2
  303. package/src/cli/cli.test.ts +23 -28
  304. package/src/cli/cli.ts +23 -10
  305. package/src/cli/mcp.test.ts +21 -7
  306. package/src/cli/plugins/tempo.ts +21 -8
  307. package/src/cli/utils.test.ts +25 -1
  308. package/src/cli/utils.ts +10 -0
  309. package/src/client/Methods.ts +5 -2
  310. package/src/client/Mppx.test-d.ts +10 -0
  311. package/src/client/Mppx.test.ts +75 -0
  312. package/src/client/Transport.ts +4 -5
  313. package/src/client/index.ts +11 -1
  314. package/src/client/internal/Fetch.test.ts +29 -4
  315. package/src/client/internal/Fetch.ts +17 -5
  316. package/src/env.d.ts +1 -1
  317. package/src/index.ts +1 -0
  318. package/src/internal/AcceptPayment.test.ts +61 -0
  319. package/src/internal/AcceptPayment.ts +21 -14
  320. package/src/mcp-sdk/client/McpClient.integration.test.ts +8 -7
  321. package/src/mcp-sdk/client/McpClient.test-d.ts +7 -0
  322. package/src/mcp-sdk/client/McpClient.ts +99 -67
  323. package/src/mcp-sdk/client/McpClient.unit.test.ts +131 -0
  324. package/src/middlewares/elysia.test.ts +8 -4
  325. package/src/middlewares/express.test.ts +8 -4
  326. package/src/middlewares/hono.test.ts +4 -4
  327. package/src/middlewares/nextjs.test.ts +8 -4
  328. package/src/proxy/Proxy.test.ts +8 -8
  329. package/src/server/Mppx.test-d.ts +54 -0
  330. package/src/server/Mppx.test.ts +200 -7
  331. package/src/server/Mppx.ts +487 -406
  332. package/src/server/Response.ts +2 -1
  333. package/src/server/Transport.ts +4 -3
  334. package/src/server/index.ts +1 -0
  335. package/src/stripe/client/Charge.test.ts +20 -5
  336. package/src/stripe/client/Charge.ts +6 -2
  337. package/src/stripe/server/Charge.test.ts +114 -1
  338. package/src/stripe/server/Charge.ts +13 -2
  339. package/src/stripe/server/internal/html.gen.ts +1 -1
  340. package/src/tempo/AccessKeyAuthorization.test.ts +4 -94
  341. package/src/tempo/Methods.test.ts +45 -17
  342. package/src/tempo/Methods.ts +22 -0
  343. package/src/tempo/PublicExports.test-d.ts +105 -0
  344. package/src/tempo/client/Charge.test.ts +85 -0
  345. package/src/tempo/client/Charge.ts +19 -2
  346. package/src/tempo/client/Methods.ts +18 -6
  347. package/src/tempo/client/index.ts +15 -4
  348. package/src/tempo/index.ts +1 -0
  349. package/src/tempo/internal/fee-payer.test.ts +241 -17
  350. package/src/tempo/internal/fee-payer.ts +150 -4
  351. package/src/tempo/internal/fee-token.test.ts +14 -9
  352. package/src/tempo/legacy/AccessKeyAuthorization.test.ts +162 -0
  353. package/src/tempo/legacy/README.md +9 -0
  354. package/src/tempo/{client → legacy/client}/ChannelOps.test.ts +6 -7
  355. package/src/tempo/{client → legacy/client}/ChannelOps.ts +22 -9
  356. package/src/tempo/{client → legacy/client}/Session.test.ts +51 -9
  357. package/src/tempo/{client → legacy/client}/Session.ts +25 -11
  358. package/src/tempo/{client → legacy/client}/SessionManager.test.ts +81 -9
  359. package/src/tempo/{client → legacy/client}/SessionManager.ts +41 -20
  360. package/src/tempo/legacy/client/index.ts +6 -0
  361. package/src/tempo/legacy/index.ts +6 -0
  362. package/src/tempo/{server → legacy/server}/Session.test.ts +45 -45
  363. package/src/tempo/{server → legacy/server}/Session.ts +32 -23
  364. package/src/tempo/legacy/server/index.ts +4 -0
  365. package/src/tempo/{session → legacy/session}/Chain.test.ts +3 -4
  366. package/src/tempo/{session → legacy/session}/Chain.ts +94 -63
  367. package/src/tempo/{session → legacy/session}/Channel.ts +1 -0
  368. package/src/tempo/legacy/session/ChannelStore.test.ts +58 -0
  369. package/src/tempo/legacy/session/ChannelStore.ts +39 -0
  370. package/src/tempo/legacy/session/Types.ts +91 -0
  371. package/src/tempo/{session → legacy/session}/Voucher.ts +12 -8
  372. package/src/tempo/{session → legacy/session}/escrow.abi.ts +1 -0
  373. package/src/tempo/legacy/session/index.ts +8 -0
  374. package/src/tempo/server/AtomicStore.test-d.ts +16 -11
  375. package/src/tempo/server/Charge.test.ts +92 -14
  376. package/src/tempo/server/Charge.ts +18 -16
  377. package/src/tempo/server/Methods.ts +54 -8
  378. package/src/tempo/server/Sse.test.ts +2 -2
  379. package/src/tempo/server/index.ts +6 -5
  380. package/src/tempo/server/internal/html.gen.ts +1 -1
  381. package/src/tempo/server/internal/request-body.test.ts +37 -4
  382. package/src/tempo/server/internal/request-body.ts +25 -6
  383. package/src/tempo/server/internal/transport.test.ts +4 -4
  384. package/src/tempo/server/internal/transport.ts +19 -10
  385. package/src/tempo/session/Snapshot.test.ts +41 -0
  386. package/src/tempo/session/Snapshot.ts +74 -0
  387. package/src/tempo/session/client/ChannelOps.test.ts +163 -0
  388. package/src/tempo/session/client/ChannelOps.ts +344 -0
  389. package/src/tempo/session/client/CredentialState.test.ts +645 -0
  390. package/src/tempo/session/client/CredentialState.ts +814 -0
  391. package/src/tempo/session/client/ReceiptCoordinator.ts +95 -0
  392. package/src/tempo/session/client/Runtime.test.ts +1092 -0
  393. package/src/tempo/session/client/Runtime.ts +986 -0
  394. package/src/tempo/session/client/Session.test.ts +734 -0
  395. package/src/tempo/session/client/Session.ts +97 -0
  396. package/src/tempo/session/client/SessionManager.test.ts +1308 -0
  397. package/src/tempo/session/client/SessionManager.ts +845 -0
  398. package/src/tempo/session/client/Transports.test.ts +837 -0
  399. package/src/tempo/session/client/Transports.ts +1292 -0
  400. package/src/tempo/session/client/index.ts +37 -0
  401. package/src/tempo/session/index.ts +7 -8
  402. package/src/tempo/session/precompile/Chain.integration.test.ts +321 -0
  403. package/src/tempo/session/precompile/Chain.test.ts +1258 -0
  404. package/src/tempo/session/precompile/Chain.ts +979 -0
  405. package/src/tempo/session/precompile/Channel.test.ts +138 -0
  406. package/src/tempo/session/precompile/Channel.ts +103 -0
  407. package/src/tempo/session/precompile/Protocol.test.ts +358 -0
  408. package/src/tempo/session/precompile/Protocol.ts +520 -0
  409. package/src/tempo/session/precompile/Voucher.test.ts +316 -0
  410. package/src/tempo/session/precompile/Voucher.ts +160 -0
  411. package/src/tempo/session/precompile/escrow.abi.ts +226 -0
  412. package/src/tempo/session/precompile/index.ts +33 -0
  413. package/src/tempo/session/server/ChannelOps.test.ts +129 -0
  414. package/src/tempo/session/server/ChannelOps.ts +157 -0
  415. package/src/tempo/session/{ChannelStore.test.ts → server/ChannelStore.test.ts} +536 -29
  416. package/src/tempo/session/server/ChannelStore.ts +835 -0
  417. package/src/tempo/session/server/CredentialVerification.test.ts +146 -0
  418. package/src/tempo/session/server/CredentialVerification.ts +710 -0
  419. package/src/tempo/session/server/MeteredStream.ts +88 -0
  420. package/src/tempo/session/server/RequestState.test.ts +531 -0
  421. package/src/tempo/session/server/RequestState.ts +499 -0
  422. package/src/tempo/session/server/Session.integration.test.ts +444 -0
  423. package/src/tempo/session/server/Session.test.ts +3253 -0
  424. package/src/tempo/session/server/Session.ts +543 -0
  425. package/src/tempo/session/server/Settlement.test.ts +242 -0
  426. package/src/tempo/session/server/Settlement.ts +470 -0
  427. package/src/tempo/session/{Sse.test.ts → server/Sse.test.ts} +37 -3
  428. package/src/tempo/session/server/Sse.ts +256 -0
  429. package/src/tempo/session/server/Transports.test.ts +346 -0
  430. package/src/tempo/session/server/Transports.ts +255 -0
  431. package/src/tempo/session/{Ws.test.ts → server/Ws.test.ts} +4 -4
  432. package/src/tempo/session/server/Ws.ts +384 -0
  433. package/src/tempo/session/server/index.ts +8 -0
  434. package/dist/tempo/client/ChannelOps.d.ts.map +0 -1
  435. package/dist/tempo/client/ChannelOps.js.map +0 -1
  436. package/dist/tempo/client/Session.d.ts.map +0 -1
  437. package/dist/tempo/client/Session.js.map +0 -1
  438. package/dist/tempo/client/SessionManager.d.ts.map +0 -1
  439. package/dist/tempo/client/SessionManager.js.map +0 -1
  440. package/dist/tempo/server/Session.d.ts.map +0 -1
  441. package/dist/tempo/server/Session.js.map +0 -1
  442. package/dist/tempo/session/Chain.d.ts.map +0 -1
  443. package/dist/tempo/session/Chain.js.map +0 -1
  444. package/dist/tempo/session/Channel.d.ts.map +0 -1
  445. package/dist/tempo/session/Channel.js.map +0 -1
  446. package/dist/tempo/session/ChannelStore.d.ts +0 -117
  447. package/dist/tempo/session/ChannelStore.d.ts.map +0 -1
  448. package/dist/tempo/session/ChannelStore.js +0 -172
  449. package/dist/tempo/session/ChannelStore.js.map +0 -1
  450. package/dist/tempo/session/Receipt.d.ts +0 -22
  451. package/dist/tempo/session/Receipt.d.ts.map +0 -1
  452. package/dist/tempo/session/Receipt.js +0 -34
  453. package/dist/tempo/session/Receipt.js.map +0 -1
  454. package/dist/tempo/session/Sse.d.ts.map +0 -1
  455. package/dist/tempo/session/Sse.js +0 -363
  456. package/dist/tempo/session/Sse.js.map +0 -1
  457. package/dist/tempo/session/Types.d.ts +0 -78
  458. package/dist/tempo/session/Types.d.ts.map +0 -1
  459. package/dist/tempo/session/Types.js.map +0 -1
  460. package/dist/tempo/session/Voucher.d.ts.map +0 -1
  461. package/dist/tempo/session/Voucher.js.map +0 -1
  462. package/dist/tempo/session/Ws.d.ts +0 -87
  463. package/dist/tempo/session/Ws.d.ts.map +0 -1
  464. package/dist/tempo/session/Ws.js +0 -443
  465. package/dist/tempo/session/Ws.js.map +0 -1
  466. package/dist/tempo/session/escrow.abi.js.map +0 -1
  467. package/src/tempo/session/ChannelStore.ts +0 -308
  468. package/src/tempo/session/Receipt.test.ts +0 -89
  469. package/src/tempo/session/Receipt.ts +0 -46
  470. package/src/tempo/session/Sse.ts +0 -462
  471. package/src/tempo/session/Types.ts +0 -86
  472. package/src/tempo/session/Ws.ts +0 -576
  473. /package/dist/tempo/{session → legacy/session}/Channel.js +0 -0
  474. /package/dist/tempo/{session → legacy/session}/Types.js +0 -0
  475. /package/src/tempo/{session → legacy/session}/Channel.test.ts +0 -0
  476. /package/src/tempo/{session → legacy/session}/Voucher.test.ts +0 -0
  477. /package/src/tempo/session/{Sse.fuzz.test.ts → server/Sse.fuzz.test.ts} +0 -0
@@ -0,0 +1,256 @@
1
+ import type { Hex } from 'viem'
2
+
3
+ import * as Constants from '../../../Constants.js'
4
+ import * as Credential from '../../../Credential.js'
5
+ import {
6
+ createSessionReceipt,
7
+ extractData,
8
+ formatMessageEvent,
9
+ formatNeedVoucherEvent,
10
+ formatReceiptEvent,
11
+ parseEvent,
12
+ readSessionChallengeAmount,
13
+ requireSessionCredentialContext,
14
+ type SseEvent,
15
+ } from '../precompile/Protocol.js'
16
+ import * as ChannelStore from './ChannelStore.js'
17
+ import { meterIterable, type SessionController } from './MeteredStream.js'
18
+
19
+ /**
20
+ * SSE (Server-Sent Events) utilities for metered streaming payments.
21
+ *
22
+ * Provides event formatting/parsing, balance polling, the core
23
+ * `serve()` loop that meters an async iterable into a ReadableStream
24
+ * of SSE events, and helpers (`toResponse`, `fromRequest`) for
25
+ * building HTTP responses from the stream.
26
+ */
27
+
28
+ export {
29
+ extractData,
30
+ formatMessageEvent,
31
+ formatNeedVoucherEvent,
32
+ formatReceiptEvent,
33
+ parseEvent,
34
+ readSessionChallengeAmount,
35
+ requireSessionCredentialContext,
36
+ type SseEvent,
37
+ } from '../precompile/Protocol.js'
38
+
39
+ /** Controller passed to manual-charge SSE generators. */
40
+ export type { SessionController } from './MeteredStream.js'
41
+
42
+ /**
43
+ * Wrap an async iterable with payment metering, producing an SSE stream.
44
+ *
45
+ * `generate` may be either:
46
+ * - An `AsyncIterable<string>` — each yielded value is automatically charged
47
+ * (one `tickCost` per value).
48
+ * - A callback `(stream: SessionController) => AsyncIterable<string>` — the
49
+ * generator controls when charges happen by calling `stream.charge()`.
50
+ *
51
+ * For each emitted value the stream:
52
+ * 1. Reserves `tickCost` from the channel's available voucher headroom
53
+ * (auto or manual).
54
+ * 2. If balance is sufficient, emits `event: message` with the value.
55
+ * 3. If balance is exhausted, emits `event: payment-need-voucher`
56
+ * and polls store until the client tops up the channel.
57
+ * 4. Commits the reserved charge immediately before the chunk is emitted.
58
+ * 5. On generator completion, emits a final `event: payment-receipt`.
59
+ *
60
+ * Returns a `ReadableStream<Uint8Array>` suitable for use as an HTTP response body.
61
+ */
62
+ export function serve(options: serve.Options): ReadableStream<Uint8Array> {
63
+ const {
64
+ store,
65
+ channelId,
66
+ challengeId,
67
+ tickCost,
68
+ generate,
69
+ pollIntervalMs = 100,
70
+ signal,
71
+ } = options
72
+
73
+ const encoder = new TextEncoder()
74
+
75
+ return new ReadableStream<Uint8Array>({
76
+ async start(controller) {
77
+ const aborted = () => signal?.aborted ?? false
78
+ const emit = (event: string) => controller.enqueue(encoder.encode(event))
79
+
80
+ try {
81
+ for await (const value of meterIterable({
82
+ store,
83
+ channelId,
84
+ tickCost,
85
+ generate,
86
+ pollIntervalMs,
87
+ prepaidUnits: options.prepaidUnits,
88
+ signal,
89
+ emitNeedVoucher: emit,
90
+ formatNeedVoucher: formatNeedVoucherEvent,
91
+ })) {
92
+ if (aborted()) break
93
+ controller.enqueue(encoder.encode(formatMessageEvent(value)))
94
+ }
95
+
96
+ if (!aborted()) {
97
+ const channel = await store.getChannel(channelId)
98
+ if (channel) {
99
+ const receipt = createSessionReceipt({
100
+ challengeId,
101
+ channelId,
102
+ acceptedCumulative: channel.highestVoucherAmount,
103
+ spent: channel.spent,
104
+ units: channel.units,
105
+ })
106
+ controller.enqueue(encoder.encode(formatReceiptEvent(receipt)))
107
+ }
108
+ }
109
+ } catch (e) {
110
+ if (!aborted()) controller.error(e)
111
+ } finally {
112
+ controller.close()
113
+ }
114
+ },
115
+ })
116
+ }
117
+
118
+ /** Type helpers for {@link serve}. */
119
+ export declare namespace serve {
120
+ type Options = {
121
+ store: ChannelStore.ChannelStore
122
+ channelId: Hex
123
+ challengeId: string
124
+ tickCost: bigint
125
+ generate: AsyncIterable<string> | ((stream: SessionController) => AsyncIterable<string>)
126
+ pollIntervalMs?: number | undefined
127
+ prepaidUnits?: number | undefined
128
+ signal?: AbortSignal | undefined
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Wrap a `ReadableStream<Uint8Array>` (from {@link serve}) in an HTTP
134
+ * `Response` with the correct SSE headers.
135
+ */
136
+ export function toResponse(body: ReadableStream<Uint8Array>): Response {
137
+ return new Response(body, {
138
+ headers: {
139
+ 'Cache-Control': 'no-cache, no-transform',
140
+ Connection: 'keep-alive',
141
+ 'Content-Type': 'text/event-stream; charset=utf-8',
142
+ },
143
+ })
144
+ }
145
+
146
+ /**
147
+ * Extract `channelId`, `challengeId`, and `tickCost` from a `Request`'s
148
+ * `Authorization: Payment …` header.
149
+ *
150
+ * This is a convenience for callers that receive a raw `Request` and need
151
+ * the parameters required by {@link serve}.
152
+ */
153
+ export function fromRequest(request: Request): fromRequest.Context {
154
+ const header = request.headers.get(Constants.Headers.authorization)
155
+ if (!header) throw new Error('Missing Authorization header.')
156
+
157
+ const payment = Credential.extractPaymentScheme(header)
158
+ if (!payment) throw new Error('Missing Payment credential in Authorization header.')
159
+
160
+ const credential = Credential.deserialize(payment)
161
+ const payload = requireSessionCredentialContext(credential.payload)
162
+ return {
163
+ challengeId: credential.challenge.id,
164
+ channelId: payload.channelId,
165
+ tickCost: readSessionChallengeAmount(credential.challenge),
166
+ }
167
+ }
168
+
169
+ /** Type helpers for {@link fromRequest}. */
170
+ export declare namespace fromRequest {
171
+ type Context = {
172
+ challengeId: string
173
+ channelId: Hex
174
+ tickCost: bigint
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Check whether a `Response` carries an SSE event stream.
180
+ *
181
+ * Returns `true` when the `Content-Type` header starts with
182
+ * `text/event-stream` (case-insensitive, ignoring charset params).
183
+ */
184
+ export function isEventStream(response: Response): boolean {
185
+ const ct = response.headers.get('content-type')
186
+ return ct?.toLowerCase().startsWith('text/event-stream') ?? false
187
+ }
188
+
189
+ /**
190
+ * Parse an SSE `Response` body into an async iterable of `data:` payloads.
191
+ *
192
+ * Yields the raw `data:` field content for each SSE event in the stream.
193
+ * Events whose data matches the `skip` predicate are silently dropped
194
+ * (e.g. `[DONE]` sentinels used by OpenAI-compatible APIs).
195
+ *
196
+ * Each yielded value typically becomes one charge tick when fed to
197
+ * {@link serve} via the SSE transport's auto-charge mode.
198
+ *
199
+ * @example
200
+ * ```ts
201
+ * const upstream = await fetch('https://api.example.com/stream')
202
+ * for await (const data of Sse.iterateData(upstream)) {
203
+ * console.log(data)
204
+ * }
205
+ * ```
206
+ */
207
+ export async function* iterateData(
208
+ response: Response,
209
+ options?: iterateData.Options,
210
+ ): AsyncGenerator<string> {
211
+ const skip = options?.skip
212
+ const body = response.body
213
+ if (!body) return
214
+
215
+ const reader = body.getReader()
216
+ const decoder = new TextDecoder()
217
+ let buffer = ''
218
+
219
+ try {
220
+ while (true) {
221
+ const { value, done } = await reader.read()
222
+ if (done) break
223
+
224
+ buffer += decoder.decode(value, { stream: true })
225
+
226
+ // Split on double-newline SSE event boundaries.
227
+ const events = buffer.split('\n\n')
228
+ // Last element may be incomplete — keep in buffer.
229
+ buffer = events.pop() ?? ''
230
+
231
+ for (const event of events) {
232
+ if (!event.trim()) continue
233
+ const data = extractData(event)
234
+ if (data === null) continue
235
+ if (skip?.(data)) continue
236
+ yield data
237
+ }
238
+ }
239
+
240
+ // Flush remaining buffer.
241
+ if (buffer.trim()) {
242
+ const data = extractData(buffer)
243
+ if (data !== null && !skip?.(data)) yield data
244
+ }
245
+ } finally {
246
+ reader.releaseLock()
247
+ }
248
+ }
249
+
250
+ /** Type helpers for {@link iterateData}. */
251
+ export declare namespace iterateData {
252
+ type Options = {
253
+ /** Predicate to skip specific data payloads (e.g. `d => d === '[DONE]'`). */
254
+ skip?: ((data: string) => boolean) | undefined
255
+ }
256
+ }
@@ -0,0 +1,346 @@
1
+ import type { Address, Hex } from 'viem'
2
+ import { describe, expect, test } from 'vp/test'
3
+
4
+ import { ChannelClosedError } from '../../../Errors.js'
5
+ import type { NeedVoucherEvent } from '../precompile/Protocol.js'
6
+ import * as ChannelStore from './ChannelStore.js'
7
+ import {
8
+ commitReservedCharges,
9
+ reserveChargeOrWait,
10
+ send,
11
+ subscribe,
12
+ toText,
13
+ type SocketEventListener,
14
+ type SocketEventMap,
15
+ } from './Transports.js'
16
+
17
+ describe('MeteredStream', () => {
18
+ const channelId = `0x${'01'.repeat(32)}` as Hex
19
+
20
+ const descriptor = {
21
+ authorizedSigner: '0x0000000000000000000000000000000000000001' as Address,
22
+ expiringNonceHash: `0x${'11'.repeat(32)}` as Hex,
23
+ operator: '0x0000000000000000000000000000000000000000' as Address,
24
+ payee: '0x0000000000000000000000000000000000000002' as Address,
25
+ payer: '0x0000000000000000000000000000000000000001' as Address,
26
+ salt: `0x${'22'.repeat(32)}` as Hex,
27
+ token: '0x20c0000000000000000000000000000000000001' as Address,
28
+ }
29
+
30
+ function channel(overrides: Partial<ChannelStore.State> = {}): ChannelStore.State {
31
+ const base: ChannelStore.State = {
32
+ authorizedSigner: descriptor.authorizedSigner,
33
+ backend: 'precompile',
34
+ chainId: 4217,
35
+ escrowContract: '0x4D50500000000000000000000000000000000000' as Address,
36
+ channelId,
37
+ closeRequestedAt: 0n,
38
+ createdAt: '2026-01-01T00:00:00.000Z',
39
+ deposit: 100n,
40
+ descriptor,
41
+ expiringNonceHash: descriptor.expiringNonceHash,
42
+ finalized: false,
43
+ highestVoucher: null,
44
+ highestVoucherAmount: 50n,
45
+ operator: descriptor.operator,
46
+ payee: descriptor.payee,
47
+ payer: descriptor.payer,
48
+ salt: descriptor.salt,
49
+ settledOnChain: 0n,
50
+ spent: 20n,
51
+ token: descriptor.token,
52
+ units: 2,
53
+ }
54
+ return { ...base, ...overrides }
55
+ }
56
+
57
+ function memoryStore(
58
+ initial: ChannelStore.State,
59
+ options: { waitForUpdate?: boolean } = {},
60
+ ): ChannelStore.ChannelStore {
61
+ let state: ChannelStore.State | null = initial
62
+ const waiters = new Set<() => void>()
63
+ const store: ChannelStore.ChannelStore = {
64
+ async getChannel() {
65
+ return state
66
+ },
67
+ async updateChannel(_channelId, fn) {
68
+ state = fn(state)
69
+ for (const waiter of waiters) waiter()
70
+ waiters.clear()
71
+ return state
72
+ },
73
+ }
74
+ if (options.waitForUpdate) {
75
+ store.waitForUpdate = () => {
76
+ return new Promise<void>((resolve) => {
77
+ waiters.add(resolve)
78
+ })
79
+ }
80
+ }
81
+ return store
82
+ }
83
+
84
+ function formatNeedVoucher(event: NeedVoucherEvent) {
85
+ return JSON.stringify(event)
86
+ }
87
+
88
+ describe('MeteredStream', () => {
89
+ test('reserveChargeOrWait returns immediately when voucher headroom is available', async () => {
90
+ const emitted: string[] = []
91
+ await reserveChargeOrWait({
92
+ amount: 10n,
93
+ channelId,
94
+ emit(message) {
95
+ emitted.push(message)
96
+ },
97
+ formatNeedVoucher,
98
+ pollIntervalMs: 1,
99
+ reservedAmount: 0n,
100
+ store: memoryStore(channel()),
101
+ })
102
+
103
+ expect(emitted).toEqual([])
104
+ })
105
+
106
+ test('reserveChargeOrWait emits need-voucher and waits for accepted headroom', async () => {
107
+ const emitted: string[] = []
108
+ const store = memoryStore(channel({ highestVoucherAmount: 25n, spent: 20n }))
109
+ const reserved = reserveChargeOrWait({
110
+ amount: 10n,
111
+ channelId,
112
+ emit(message) {
113
+ emitted.push(message)
114
+ },
115
+ formatNeedVoucher,
116
+ pollIntervalMs: 1,
117
+ reservedAmount: 0n,
118
+ store,
119
+ })
120
+
121
+ await Promise.resolve()
122
+ expect(emitted.map((item) => JSON.parse(item))).toEqual([
123
+ {
124
+ channelId,
125
+ requiredCumulative: '30',
126
+ acceptedCumulative: '25',
127
+ deposit: '100',
128
+ },
129
+ ])
130
+
131
+ await store.updateChannel(channelId, (current) =>
132
+ current ? { ...current, highestVoucherAmount: 30n } : current,
133
+ )
134
+ await reserved
135
+ })
136
+
137
+ test('reserveChargeOrWait observes updates that happen before wait registration', async () => {
138
+ const emitted: string[] = []
139
+ const store = memoryStore(channel({ highestVoucherAmount: 25n, spent: 20n }), {
140
+ waitForUpdate: true,
141
+ })
142
+
143
+ await reserveChargeOrWait({
144
+ amount: 10n,
145
+ channelId,
146
+ async emit(message) {
147
+ emitted.push(message)
148
+ await store.updateChannel(channelId, (current) =>
149
+ current ? { ...current, highestVoucherAmount: 30n } : current,
150
+ )
151
+ },
152
+ formatNeedVoucher,
153
+ pollIntervalMs: 1,
154
+ reservedAmount: 0n,
155
+ store,
156
+ })
157
+
158
+ expect(emitted).toHaveLength(1)
159
+ })
160
+
161
+ test('commitReservedCharges increments spend and units', async () => {
162
+ const store = memoryStore(channel({ spent: 20n, units: 2, highestVoucherAmount: 50n }))
163
+
164
+ await commitReservedCharges({ amount: 10n, channelId, store, units: 1 })
165
+
166
+ expect(await store.getChannel(channelId)).toMatchObject({ spent: 30n, units: 3 })
167
+ })
168
+
169
+ test('commitReservedCharges rejects when reserved coverage is no longer available', async () => {
170
+ await expect(
171
+ commitReservedCharges({
172
+ amount: 40n,
173
+ channelId,
174
+ store: memoryStore(channel({ spent: 20n, highestVoucherAmount: 50n })),
175
+ units: 1,
176
+ }),
177
+ ).rejects.toThrow('reserved voucher coverage is no longer available')
178
+ })
179
+
180
+ test('commitReservedCharges rejects closed channels', async () => {
181
+ await expect(
182
+ commitReservedCharges({
183
+ amount: 10n,
184
+ channelId,
185
+ store: memoryStore(channel({ finalized: true })),
186
+ units: 1,
187
+ }),
188
+ ).rejects.toThrow(ChannelClosedError)
189
+ })
190
+ })
191
+ })
192
+
193
+ describe('SocketTransport', () => {
194
+ class BrowserSocket {
195
+ sent: string[] = []
196
+ listeners = {
197
+ close: new Set<SocketEventListener<'close'>>(),
198
+ error: new Set<SocketEventListener<'error'>>(),
199
+ message: new Set<SocketEventListener<'message'>>(),
200
+ }
201
+
202
+ addEventListener<type extends keyof SocketEventMap>(
203
+ type: type,
204
+ listener: SocketEventListener<type>,
205
+ ) {
206
+ this.listeners[type].add(listener as never)
207
+ }
208
+
209
+ close() {
210
+ this.emit('close', { type: 'close' })
211
+ }
212
+
213
+ emit<type extends keyof SocketEventMap>(type: type, event: SocketEventMap[type]) {
214
+ for (const listener of this.listeners[type]) {
215
+ if (typeof listener === 'function') listener(event as never)
216
+ else listener.handleEvent(event as never)
217
+ }
218
+ }
219
+
220
+ removeEventListener<type extends keyof SocketEventMap>(
221
+ type: type,
222
+ listener: SocketEventListener<type>,
223
+ ) {
224
+ this.listeners[type].delete(listener as never)
225
+ }
226
+
227
+ send(data: string) {
228
+ this.sent.push(data)
229
+ }
230
+ }
231
+
232
+ class NodeSocket {
233
+ sent: string[] = []
234
+ listeners = {
235
+ close: new Set<(event: SocketEventMap['close']) => void>(),
236
+ error: new Set<(event: SocketEventMap['error']) => void>(),
237
+ message: new Set<(event: SocketEventMap['message']) => void>(),
238
+ }
239
+
240
+ close() {
241
+ this.emit('close', { type: 'close' })
242
+ }
243
+
244
+ emit<type extends keyof SocketEventMap>(type: type, event: SocketEventMap[type]) {
245
+ for (const listener of this.listeners[type]) listener(event as never)
246
+ }
247
+
248
+ off<type extends keyof SocketEventMap>(
249
+ type: type,
250
+ listener: (event: SocketEventMap[type]) => void,
251
+ ) {
252
+ this.listeners[type].delete(listener as never)
253
+ }
254
+
255
+ on<type extends keyof SocketEventMap>(
256
+ type: type,
257
+ listener: (event: SocketEventMap[type]) => void,
258
+ ) {
259
+ this.listeners[type].add(listener as never)
260
+ }
261
+
262
+ send(data: string) {
263
+ this.sent.push(data)
264
+ }
265
+ }
266
+
267
+ describe('SocketTransport', () => {
268
+ test('subscribe handles browser-style socket events', () => {
269
+ const socket = new BrowserSocket()
270
+ const messages: unknown[] = []
271
+ let closed = 0
272
+ let errors = 0
273
+
274
+ const unsubscribe = subscribe(socket, {
275
+ close() {
276
+ closed++
277
+ },
278
+ error() {
279
+ errors++
280
+ },
281
+ message(value) {
282
+ messages.push(value)
283
+ },
284
+ })
285
+
286
+ socket.emit('message', { data: 'hello', type: 'message' })
287
+ socket.emit('error', { type: 'error' })
288
+ socket.emit('close', { type: 'close' })
289
+ unsubscribe()
290
+ socket.emit('message', { data: 'ignored', type: 'message' })
291
+
292
+ expect(messages).toEqual(['hello'])
293
+ expect(errors).toBe(1)
294
+ expect(closed).toBe(1)
295
+ })
296
+
297
+ test('subscribe handles node-style socket events', () => {
298
+ const socket = new NodeSocket()
299
+ const messages: unknown[] = []
300
+
301
+ const unsubscribe = subscribe(socket, {
302
+ close() {},
303
+ error() {},
304
+ message(value) {
305
+ messages.push(value)
306
+ },
307
+ })
308
+
309
+ socket.emit('message', { data: 'hello', type: 'message' })
310
+ unsubscribe()
311
+ socket.emit('message', { data: 'ignored', type: 'message' })
312
+
313
+ expect(messages).toEqual([{ data: 'hello', type: 'message' }])
314
+ })
315
+
316
+ test('subscribe rejects unsupported socket implementations', () => {
317
+ expect(() =>
318
+ subscribe({ close() {}, send() {} }, { close() {}, error() {}, message() {} }),
319
+ ).toThrow('unsupported websocket implementation')
320
+ })
321
+
322
+ test('send supports sync and async socket implementations', async () => {
323
+ const syncSocket = new BrowserSocket()
324
+ const asyncSocket = {
325
+ close() {},
326
+ sent: [] as string[],
327
+ async send(data: string) {
328
+ this.sent.push(data)
329
+ },
330
+ }
331
+
332
+ await send(syncSocket, 'sync')
333
+ await send(asyncSocket, 'async')
334
+
335
+ expect(syncSocket.sent).toEqual(['sync'])
336
+ expect(asyncSocket.sent).toEqual(['async'])
337
+ })
338
+
339
+ test('toText normalizes common message payloads', () => {
340
+ expect(toText('hello')).toBe('hello')
341
+ expect(toText(new TextEncoder().encode('bytes'))).toBe('bytes')
342
+ expect(toText(new TextEncoder().encode('buffer').buffer)).toBe('buffer')
343
+ expect(toText({ data: 'object' })).toBeNull()
344
+ })
345
+ })
346
+ })