mppx 0.8.15 → 0.8.17

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 (464) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/Challenge.d.ts.map +1 -1
  3. package/dist/Challenge.js +40 -35
  4. package/dist/Challenge.js.map +1 -1
  5. package/dist/Constants.d.ts +17 -17
  6. package/dist/Constants.d.ts.map +1 -1
  7. package/dist/Credential.d.ts.map +1 -1
  8. package/dist/Errors.d.ts +3 -3
  9. package/dist/Errors.d.ts.map +1 -1
  10. package/dist/Errors.js.map +1 -1
  11. package/dist/Method.d.ts +35 -5
  12. package/dist/Method.d.ts.map +1 -1
  13. package/dist/Method.js +7 -22
  14. package/dist/Method.js.map +1 -1
  15. package/dist/Store.d.ts +19 -0
  16. package/dist/Store.d.ts.map +1 -1
  17. package/dist/Store.js +43 -0
  18. package/dist/Store.js.map +1 -1
  19. package/dist/attestation/Client.d.ts +17 -0
  20. package/dist/attestation/Client.d.ts.map +1 -0
  21. package/dist/attestation/Client.js +28 -0
  22. package/dist/attestation/Client.js.map +1 -0
  23. package/dist/attestation/Constants.d.ts +15 -0
  24. package/dist/attestation/Constants.d.ts.map +1 -0
  25. package/dist/attestation/Constants.js +15 -0
  26. package/dist/attestation/Constants.js.map +1 -0
  27. package/dist/attestation/Server.d.ts +4 -0
  28. package/dist/attestation/Server.d.ts.map +1 -0
  29. package/dist/attestation/Server.js +8 -0
  30. package/dist/attestation/Server.js.map +1 -0
  31. package/dist/attestation/Types.d.ts +76 -0
  32. package/dist/attestation/Types.d.ts.map +1 -0
  33. package/dist/attestation/Types.js +2 -0
  34. package/dist/attestation/Types.js.map +1 -0
  35. package/dist/attestation/index.d.ts +6 -0
  36. package/dist/attestation/index.d.ts.map +1 -0
  37. package/dist/attestation/index.js +6 -0
  38. package/dist/attestation/index.js.map +1 -0
  39. package/dist/attestation/internal/HttpMessageSignature.d.ts +73 -0
  40. package/dist/attestation/internal/HttpMessageSignature.d.ts.map +1 -0
  41. package/dist/attestation/internal/HttpMessageSignature.js +320 -0
  42. package/dist/attestation/internal/HttpMessageSignature.js.map +1 -0
  43. package/dist/attestation/internal/SigningContext.d.ts +4 -0
  44. package/dist/attestation/internal/SigningContext.d.ts.map +1 -0
  45. package/dist/attestation/internal/SigningContext.js +11 -0
  46. package/dist/attestation/internal/SigningContext.js.map +1 -0
  47. package/dist/cli/account.d.ts +14 -14
  48. package/dist/cli/account.d.ts.map +1 -1
  49. package/dist/cli/cli.d.ts +6 -6
  50. package/dist/cli/cli.d.ts.map +1 -1
  51. package/dist/cli/cli.js +1 -0
  52. package/dist/cli/cli.js.map +1 -1
  53. package/dist/cli/plugins/tempo.js.map +1 -1
  54. package/dist/cli/sessions/commands.d.ts +4 -4
  55. package/dist/cli/sessions/commands.d.ts.map +1 -1
  56. package/dist/cli/sessions/commands.js.map +1 -1
  57. package/dist/cli/sessions/store.d.ts.map +1 -1
  58. package/dist/cli/sessions/store.js.map +1 -1
  59. package/dist/cli/utils.d.ts +1 -1
  60. package/dist/cli/utils.d.ts.map +1 -1
  61. package/dist/cli/utils.js.map +1 -1
  62. package/dist/cli/validate/discovery.js.map +1 -1
  63. package/dist/cli/validate/index.d.ts +3 -3
  64. package/dist/cli/validate/index.d.ts.map +1 -1
  65. package/dist/client/Mppx.d.ts +3 -0
  66. package/dist/client/Mppx.d.ts.map +1 -1
  67. package/dist/client/Mppx.js +13 -5
  68. package/dist/client/Mppx.js.map +1 -1
  69. package/dist/client/Transport.d.ts +3 -5
  70. package/dist/client/Transport.d.ts.map +1 -1
  71. package/dist/client/Transport.js +1 -17
  72. package/dist/client/Transport.js.map +1 -1
  73. package/dist/client/internal/Fetch.d.ts.map +1 -1
  74. package/dist/client/internal/Fetch.js +2 -6
  75. package/dist/client/internal/Fetch.js.map +1 -1
  76. package/dist/client/node.js.map +1 -1
  77. package/dist/discovery/OpenApi.d.ts +3 -5
  78. package/dist/discovery/OpenApi.d.ts.map +1 -1
  79. package/dist/discovery/OpenApi.js +13 -34
  80. package/dist/discovery/OpenApi.js.map +1 -1
  81. package/dist/discovery/internal/Metadata.d.ts +20 -0
  82. package/dist/discovery/internal/Metadata.d.ts.map +1 -0
  83. package/dist/discovery/internal/Metadata.js +36 -0
  84. package/dist/discovery/internal/Metadata.js.map +1 -0
  85. package/dist/evm/Methods.d.ts +11 -11
  86. package/dist/evm/Methods.d.ts.map +1 -1
  87. package/dist/evm/Types.d.ts +17 -17
  88. package/dist/evm/Types.d.ts.map +1 -1
  89. package/dist/evm/client/Charge.d.ts +11 -11
  90. package/dist/evm/client/Methods.d.ts +11 -11
  91. package/dist/evm/server/Charge.d.ts +1 -1
  92. package/dist/evm/server/Charge.d.ts.map +1 -1
  93. package/dist/evm/server/Charge.js +2 -0
  94. package/dist/evm/server/Charge.js.map +1 -1
  95. package/dist/evm/server/Methods.d.ts +11 -11
  96. package/dist/internal/HeaderCodec.d.ts +2 -2
  97. package/dist/internal/HeaderCodec.d.ts.map +1 -1
  98. package/dist/internal/types.d.ts +6 -0
  99. package/dist/internal/types.d.ts.map +1 -1
  100. package/dist/middlewares/elysia.js.map +1 -1
  101. package/dist/middlewares/express.js.map +1 -1
  102. package/dist/middlewares/hono.js.map +1 -1
  103. package/dist/middlewares/nextjs.js.map +1 -1
  104. package/dist/proxy/Proxy.d.ts +2 -0
  105. package/dist/proxy/Proxy.d.ts.map +1 -1
  106. package/dist/proxy/Proxy.js +117 -9
  107. package/dist/proxy/Proxy.js.map +1 -1
  108. package/dist/proxy/Service.d.ts +36 -1
  109. package/dist/proxy/Service.d.ts.map +1 -1
  110. package/dist/proxy/Service.js +9 -8
  111. package/dist/proxy/Service.js.map +1 -1
  112. package/dist/proxy/services/anthropic.d.ts.map +1 -1
  113. package/dist/proxy/services/anthropic.js +1 -0
  114. package/dist/proxy/services/anthropic.js.map +1 -1
  115. package/dist/proxy/services/openai.d.ts.map +1 -1
  116. package/dist/proxy/services/openai.js +1 -0
  117. package/dist/proxy/services/openai.js.map +1 -1
  118. package/dist/proxy/services/stripe.d.ts.map +1 -1
  119. package/dist/proxy/services/stripe.js +1 -0
  120. package/dist/proxy/services/stripe.js.map +1 -1
  121. package/dist/server/Mppx.d.ts +75 -13
  122. package/dist/server/Mppx.d.ts.map +1 -1
  123. package/dist/server/Mppx.js +201 -23
  124. package/dist/server/Mppx.js.map +1 -1
  125. package/dist/server/internal/html/config.d.ts +9 -9
  126. package/dist/server/internal/html/config.d.ts.map +1 -1
  127. package/dist/server/internal/html/constants.d.ts +17 -17
  128. package/dist/server/internal/html/constants.d.ts.map +1 -1
  129. package/dist/stripe/Methods.d.ts +10 -10
  130. package/dist/stripe/Methods.d.ts.map +1 -1
  131. package/dist/stripe/client/Charge.d.ts +10 -10
  132. package/dist/stripe/client/Methods.d.ts +10 -10
  133. package/dist/stripe/internal/constants.d.ts +1 -1
  134. package/dist/stripe/internal/constants.js +1 -1
  135. package/dist/stripe/internal/types.d.ts +1 -0
  136. package/dist/stripe/internal/types.d.ts.map +1 -1
  137. package/dist/stripe/server/Charge.d.ts +11 -11
  138. package/dist/stripe/server/Charge.d.ts.map +1 -1
  139. package/dist/stripe/server/Charge.js +49 -1
  140. package/dist/stripe/server/Charge.js.map +1 -1
  141. package/dist/stripe/server/Methods.d.ts +137 -48
  142. package/dist/stripe/server/Methods.d.ts.map +1 -1
  143. package/dist/stripe/server/Methods.js +259 -4
  144. package/dist/stripe/server/Methods.js.map +1 -1
  145. package/dist/stripe/server/internal/deposit-address.d.ts +13 -0
  146. package/dist/stripe/server/internal/deposit-address.d.ts.map +1 -0
  147. package/dist/stripe/server/internal/deposit-address.js +28 -0
  148. package/dist/stripe/server/internal/deposit-address.js.map +1 -0
  149. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  150. package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
  151. package/dist/stripe/server/internal/html.gen.js +1 -1
  152. package/dist/stripe/server/internal/html.gen.js.map +1 -1
  153. package/dist/stripe/server/internal/record-payment.d.ts +15 -0
  154. package/dist/stripe/server/internal/record-payment.d.ts.map +1 -0
  155. package/dist/stripe/server/internal/record-payment.js +42 -0
  156. package/dist/stripe/server/internal/record-payment.js.map +1 -0
  157. package/dist/stripe/server/internal/request.d.ts +25 -0
  158. package/dist/stripe/server/internal/request.d.ts.map +1 -0
  159. package/dist/stripe/server/internal/request.js +29 -0
  160. package/dist/stripe/server/internal/request.js.map +1 -0
  161. package/dist/tap/Constants.d.ts +28 -0
  162. package/dist/tap/Constants.d.ts.map +1 -0
  163. package/dist/tap/Constants.js +32 -0
  164. package/dist/tap/Constants.js.map +1 -0
  165. package/dist/tap/Types.d.ts +11 -0
  166. package/dist/tap/Types.d.ts.map +1 -0
  167. package/dist/tap/Types.js +2 -0
  168. package/dist/tap/Types.js.map +1 -0
  169. package/dist/tap/client/index.d.ts +26 -0
  170. package/dist/tap/client/index.d.ts.map +1 -0
  171. package/dist/tap/client/index.js +33 -0
  172. package/dist/tap/client/index.js.map +1 -0
  173. package/dist/tap/index.d.ts +5 -0
  174. package/dist/tap/index.d.ts.map +1 -0
  175. package/dist/tap/index.js +5 -0
  176. package/dist/tap/index.js.map +1 -0
  177. package/dist/tap/server/index.d.ts +20 -0
  178. package/dist/tap/server/index.d.ts.map +1 -0
  179. package/dist/tap/server/index.js +36 -0
  180. package/dist/tap/server/index.js.map +1 -0
  181. package/dist/tempo/Attribution.d.ts +1 -1
  182. package/dist/tempo/Methods.d.ts +60 -60
  183. package/dist/tempo/Methods.d.ts.map +1 -1
  184. package/dist/tempo/Proof.d.ts +16 -16
  185. package/dist/tempo/client/Charge.d.ts +19 -19
  186. package/dist/tempo/client/Charge.d.ts.map +1 -1
  187. package/dist/tempo/client/Charge.js +1 -7
  188. package/dist/tempo/client/Charge.js.map +1 -1
  189. package/dist/tempo/client/Methods.d.ts +38 -38
  190. package/dist/tempo/client/Methods.js.map +1 -1
  191. package/dist/tempo/client/Subscription.d.ts +23 -23
  192. package/dist/tempo/client/Subscription.js.map +1 -1
  193. package/dist/tempo/internal/address.d.ts +2 -1
  194. package/dist/tempo/internal/address.d.ts.map +1 -1
  195. package/dist/tempo/internal/address.js +3 -1
  196. package/dist/tempo/internal/address.js.map +1 -1
  197. package/dist/tempo/internal/auto-swap.d.ts.map +1 -1
  198. package/dist/tempo/internal/auto-swap.js +4 -19
  199. package/dist/tempo/internal/auto-swap.js.map +1 -1
  200. package/dist/tempo/internal/defaults.d.ts +6 -6
  201. package/dist/tempo/internal/defaults.d.ts.map +1 -1
  202. package/dist/tempo/internal/fee-payer.d.ts +12 -12
  203. package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
  204. package/dist/tempo/internal/fee-payer.js.map +1 -1
  205. package/dist/tempo/internal/proof.d.ts +17 -17
  206. package/dist/tempo/internal/proof.d.ts.map +1 -1
  207. package/dist/tempo/legacy/client/Session.d.ts +20 -20
  208. package/dist/tempo/legacy/client/Session.js.map +1 -1
  209. package/dist/tempo/legacy/client/SessionManager.js.map +1 -1
  210. package/dist/tempo/legacy/index.d.ts +1 -3
  211. package/dist/tempo/legacy/index.d.ts.map +1 -1
  212. package/dist/tempo/legacy/index.js +1 -3
  213. package/dist/tempo/legacy/index.js.map +1 -1
  214. package/dist/tempo/legacy/session/escrow.abi.d.ts +374 -374
  215. package/dist/tempo/legacy/session/escrow.abi.d.ts.map +1 -1
  216. package/dist/tempo/server/Charge.d.ts +25 -11
  217. package/dist/tempo/server/Charge.d.ts.map +1 -1
  218. package/dist/tempo/server/Charge.js +44 -39
  219. package/dist/tempo/server/Charge.js.map +1 -1
  220. package/dist/tempo/server/Methods.d.ts +37 -52
  221. package/dist/tempo/server/Methods.d.ts.map +1 -1
  222. package/dist/tempo/server/Methods.js +0 -14
  223. package/dist/tempo/server/Methods.js.map +1 -1
  224. package/dist/tempo/server/Relay.d.ts +1 -1
  225. package/dist/tempo/server/Relay.d.ts.map +1 -1
  226. package/dist/tempo/server/Relay.js +2 -2
  227. package/dist/tempo/server/Relay.js.map +1 -1
  228. package/dist/tempo/server/Subscription.d.ts +25 -25
  229. package/dist/tempo/server/Subscription.d.ts.map +1 -1
  230. package/dist/tempo/server/Subscription.js +2 -0
  231. package/dist/tempo/server/Subscription.js.map +1 -1
  232. package/dist/tempo/server/index.d.ts +1 -1
  233. package/dist/tempo/server/index.d.ts.map +1 -1
  234. package/dist/tempo/server/index.js +1 -1
  235. package/dist/tempo/server/index.js.map +1 -1
  236. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  237. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
  238. package/dist/tempo/server/internal/html.gen.js +1 -1
  239. package/dist/tempo/server/internal/html.gen.js.map +1 -1
  240. package/dist/tempo/server/internal/transport.d.ts +2 -0
  241. package/dist/tempo/server/internal/transport.d.ts.map +1 -1
  242. package/dist/tempo/server/internal/transport.js +9 -2
  243. package/dist/tempo/server/internal/transport.js.map +1 -1
  244. package/dist/tempo/session/client/ChannelOps.js.map +1 -1
  245. package/dist/tempo/session/client/CredentialState.d.ts +1 -1
  246. package/dist/tempo/session/client/CredentialState.js.map +1 -1
  247. package/dist/tempo/session/client/ReceiptCoordinator.js.map +1 -1
  248. package/dist/tempo/session/client/Runtime.js.map +1 -1
  249. package/dist/tempo/session/client/Session.d.ts +19 -19
  250. package/dist/tempo/session/client/Session.js.map +1 -1
  251. package/dist/tempo/session/client/SessionManager.js.map +1 -1
  252. package/dist/tempo/session/client/Transports.d.ts.map +1 -1
  253. package/dist/tempo/session/precompile/Chain.d.ts +27 -0
  254. package/dist/tempo/session/precompile/Chain.d.ts.map +1 -1
  255. package/dist/tempo/session/precompile/Chain.js +68 -7
  256. package/dist/tempo/session/precompile/Chain.js.map +1 -1
  257. package/dist/tempo/session/precompile/Protocol.js.map +1 -1
  258. package/dist/tempo/session/precompile/Voucher.d.ts +4 -4
  259. package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -1
  260. package/dist/tempo/session/precompile/escrow.abi.d.ts +306 -306
  261. package/dist/tempo/session/precompile/escrow.abi.d.ts.map +1 -1
  262. package/dist/tempo/session/server/ChannelStore.d.ts +9 -0
  263. package/dist/tempo/session/server/ChannelStore.d.ts.map +1 -1
  264. package/dist/tempo/session/server/ChannelStore.js +16 -8
  265. package/dist/tempo/session/server/ChannelStore.js.map +1 -1
  266. package/dist/tempo/session/server/CredentialVerification.d.ts +30 -13
  267. package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -1
  268. package/dist/tempo/session/server/CredentialVerification.js +207 -13
  269. package/dist/tempo/session/server/CredentialVerification.js.map +1 -1
  270. package/dist/tempo/session/server/MeteredStream.d.ts +3 -0
  271. package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -1
  272. package/dist/tempo/session/server/MeteredStream.js +3 -1
  273. package/dist/tempo/session/server/MeteredStream.js.map +1 -1
  274. package/dist/tempo/session/server/Session.d.ts +32 -21
  275. package/dist/tempo/session/server/Session.d.ts.map +1 -1
  276. package/dist/tempo/session/server/Session.js +116 -53
  277. package/dist/tempo/session/server/Session.js.map +1 -1
  278. package/dist/tempo/session/server/Sse.d.ts +3 -1
  279. package/dist/tempo/session/server/Sse.d.ts.map +1 -1
  280. package/dist/tempo/session/server/Sse.js +2 -1
  281. package/dist/tempo/session/server/Sse.js.map +1 -1
  282. package/dist/tempo/session/server/Transports.d.ts +1 -1
  283. package/dist/tempo/session/server/Transports.d.ts.map +1 -1
  284. package/dist/tempo/session/server/Transports.js +1 -0
  285. package/dist/tempo/session/server/Transports.js.map +1 -1
  286. package/dist/tempo/session/server/Ws.d.ts +6 -1
  287. package/dist/tempo/session/server/Ws.d.ts.map +1 -1
  288. package/dist/tempo/session/server/Ws.js +1 -0
  289. package/dist/tempo/session/server/Ws.js.map +1 -1
  290. package/dist/tempo/subscription/KeyAuthorization.d.ts +39 -39
  291. package/dist/tempo/subscription/KeyAuthorization.d.ts.map +1 -1
  292. package/dist/viem/Account.d.ts.map +1 -1
  293. package/dist/viem/Account.js.map +1 -1
  294. package/dist/web-bot-auth/Constants.d.ts +20 -0
  295. package/dist/web-bot-auth/Constants.d.ts.map +1 -0
  296. package/dist/web-bot-auth/Constants.js +24 -0
  297. package/dist/web-bot-auth/Constants.js.map +1 -0
  298. package/dist/web-bot-auth/Types.d.ts +10 -0
  299. package/dist/web-bot-auth/Types.d.ts.map +1 -0
  300. package/dist/web-bot-auth/Types.js +2 -0
  301. package/dist/web-bot-auth/Types.js.map +1 -0
  302. package/dist/web-bot-auth/client/index.d.ts +29 -0
  303. package/dist/web-bot-auth/client/index.d.ts.map +1 -0
  304. package/dist/web-bot-auth/client/index.js +64 -0
  305. package/dist/web-bot-auth/client/index.js.map +1 -0
  306. package/dist/web-bot-auth/index.d.ts +7 -0
  307. package/dist/web-bot-auth/index.d.ts.map +1 -0
  308. package/dist/web-bot-auth/index.js +7 -0
  309. package/dist/web-bot-auth/index.js.map +1 -0
  310. package/dist/web-bot-auth/internal/JwkThumbprint.d.ts +5 -0
  311. package/dist/web-bot-auth/internal/JwkThumbprint.d.ts.map +1 -0
  312. package/dist/web-bot-auth/internal/JwkThumbprint.js +20 -0
  313. package/dist/web-bot-auth/internal/JwkThumbprint.js.map +1 -0
  314. package/dist/web-bot-auth/internal/SignatureAgent.d.ts +3 -0
  315. package/dist/web-bot-auth/internal/SignatureAgent.d.ts.map +1 -0
  316. package/dist/web-bot-auth/internal/SignatureAgent.js +18 -0
  317. package/dist/web-bot-auth/internal/SignatureAgent.js.map +1 -0
  318. package/dist/web-bot-auth/server/index.d.ts +35 -0
  319. package/dist/web-bot-auth/server/index.d.ts.map +1 -0
  320. package/dist/web-bot-auth/server/index.js +88 -0
  321. package/dist/web-bot-auth/server/index.js.map +1 -0
  322. package/dist/x402/Assets.d.ts.map +1 -1
  323. package/dist/x402/Types.d.ts +6 -6
  324. package/dist/x402/Types.d.ts.map +1 -1
  325. package/dist/x402/server/EvmCharge.d.ts.map +1 -1
  326. package/dist/x402/server/EvmCharge.js +32 -20
  327. package/dist/x402/server/EvmCharge.js.map +1 -1
  328. package/package.json +19 -3
  329. package/src/Challenge.fuzz.test.ts +80 -47
  330. package/src/Challenge.test.ts +65 -0
  331. package/src/Challenge.ts +39 -35
  332. package/src/Method.ts +82 -68
  333. package/src/Store.test-d.ts +16 -0
  334. package/src/Store.test.ts +50 -0
  335. package/src/Store.ts +68 -0
  336. package/src/attestation/Attestation.integration.test.ts +208 -0
  337. package/src/attestation/Client.test.ts +83 -0
  338. package/src/attestation/Client.ts +34 -0
  339. package/src/attestation/Constants.ts +15 -0
  340. package/src/attestation/Server.test.ts +60 -0
  341. package/src/attestation/Server.ts +12 -0
  342. package/src/attestation/Types.test-d.ts +54 -0
  343. package/src/attestation/Types.ts +77 -0
  344. package/src/attestation/index.ts +5 -0
  345. package/src/attestation/internal/HttpMessageSignature.test.ts +101 -0
  346. package/src/attestation/internal/HttpMessageSignature.ts +429 -0
  347. package/src/attestation/internal/SigningContext.ts +13 -0
  348. package/src/bin.js +2 -0
  349. package/src/cli/cli.ts +1 -0
  350. package/src/client/Mppx.test-d.ts +17 -0
  351. package/src/client/Mppx.test.ts +14 -14
  352. package/src/client/Mppx.ts +25 -4
  353. package/src/client/Transport.test.ts +12 -45
  354. package/src/client/Transport.ts +3 -24
  355. package/src/client/internal/Fetch.ts +2 -6
  356. package/src/discovery/OpenApi.test.ts +170 -0
  357. package/src/discovery/OpenApi.ts +16 -48
  358. package/src/discovery/internal/Metadata.ts +51 -0
  359. package/src/evm/server/Charge.ts +6 -1
  360. package/src/internal/AcceptPayment.test.ts +12 -8
  361. package/src/internal/types.ts +9 -0
  362. package/src/mcp/client/McpClient.integration.test.ts +19 -32
  363. package/src/middlewares/elysia.test.ts +2 -12
  364. package/src/middlewares/express.test.ts +2 -12
  365. package/src/middlewares/hono.test.ts +6 -9
  366. package/src/middlewares/nextjs.test.ts +2 -12
  367. package/src/proxy/Proxy.test.ts +26 -19
  368. package/src/proxy/Proxy.ts +147 -9
  369. package/src/proxy/Proxy.upstreamError.test.ts +364 -0
  370. package/src/proxy/Service.test.ts +73 -3
  371. package/src/proxy/Service.ts +50 -23
  372. package/src/proxy/services/anthropic.ts +1 -0
  373. package/src/proxy/services/hooks.test-d.ts +14 -0
  374. package/src/proxy/services/hooks.test.ts +44 -0
  375. package/src/proxy/services/openai.ts +1 -0
  376. package/src/proxy/services/stripe.ts +1 -0
  377. package/src/server/Methods.test-d.ts +88 -0
  378. package/src/server/Methods.test.ts +69 -0
  379. package/src/server/Mppx.test-d.ts +141 -7
  380. package/src/server/Mppx.test.ts +1017 -119
  381. package/src/server/Mppx.ts +350 -54
  382. package/src/stripe/internal/constants.ts +1 -1
  383. package/src/stripe/internal/types.ts +1 -0
  384. package/src/stripe/server/Charge.test-d.ts +16 -1
  385. package/src/stripe/server/Charge.test.ts +79 -0
  386. package/src/stripe/server/Charge.ts +54 -1
  387. package/src/stripe/server/Methods.test-d.ts +157 -0
  388. package/src/stripe/server/Methods.test.ts +403 -0
  389. package/src/stripe/server/Methods.ts +423 -6
  390. package/src/stripe/server/internal/deposit-address.ts +57 -0
  391. package/src/stripe/server/internal/html/node_modules/.bin/mppx +3 -3
  392. package/src/stripe/server/internal/html/package.json +1 -1
  393. package/src/stripe/server/internal/html.gen.ts +1 -1
  394. package/src/stripe/server/internal/record-payment.ts +66 -0
  395. package/src/stripe/server/internal/request.ts +49 -0
  396. package/src/tap/Constants.ts +32 -0
  397. package/src/tap/Tap.test.ts +125 -0
  398. package/src/tap/Types.ts +11 -0
  399. package/src/tap/client/index.ts +54 -0
  400. package/src/tap/index.ts +4 -0
  401. package/src/tap/server/index.ts +48 -0
  402. package/src/tempo/PublicExports.test-d.ts +3 -3
  403. package/src/tempo/client/Charge.ts +1 -19
  404. package/src/tempo/internal/address.test.ts +42 -0
  405. package/src/tempo/internal/address.ts +3 -3
  406. package/src/tempo/internal/auto-swap.test.ts +4 -4
  407. package/src/tempo/internal/auto-swap.ts +7 -50
  408. package/src/tempo/legacy/README.md +5 -5
  409. package/src/tempo/legacy/index.ts +1 -3
  410. package/src/tempo/server/AtomicStore.test-d.ts +13 -0
  411. package/src/tempo/server/Charge.test.ts +19 -84
  412. package/src/tempo/server/Charge.ts +83 -55
  413. package/src/tempo/server/Methods.ts +0 -20
  414. package/src/tempo/server/Relay.test.ts +5 -5
  415. package/src/tempo/server/Relay.ts +2 -2
  416. package/src/tempo/server/Subscription.ts +4 -1
  417. package/src/tempo/server/index.ts +1 -1
  418. package/src/tempo/server/internal/html/node_modules/.bin/mppx +3 -3
  419. package/src/tempo/server/internal/html/package.json +1 -1
  420. package/src/tempo/server/internal/html.gen.ts +1 -1
  421. package/src/tempo/server/internal/transport.test.ts +22 -3
  422. package/src/tempo/server/internal/transport.ts +15 -3
  423. package/src/tempo/session/README.md +112 -0
  424. package/src/tempo/session/precompile/Chain.ts +131 -11
  425. package/src/tempo/session/server/ChannelStore.ts +33 -12
  426. package/src/tempo/session/server/CredentialVerification.test.ts +4 -4
  427. package/src/tempo/session/server/CredentialVerification.ts +340 -28
  428. package/src/tempo/session/server/MeteredStream.ts +5 -1
  429. package/src/tempo/session/server/Session.test.ts +428 -43
  430. package/src/tempo/session/server/Session.ts +159 -60
  431. package/src/tempo/session/server/Sse.test.ts +77 -0
  432. package/src/tempo/session/server/Sse.ts +8 -1
  433. package/src/tempo/session/server/Transports.ts +2 -1
  434. package/src/tempo/session/server/Ws.test.ts +171 -0
  435. package/src/tempo/session/server/Ws.ts +7 -1
  436. package/src/web-bot-auth/Constants.ts +24 -0
  437. package/src/web-bot-auth/Types.ts +9 -0
  438. package/src/web-bot-auth/WebBotAuth.test.ts +182 -0
  439. package/src/web-bot-auth/client/index.ts +84 -0
  440. package/src/web-bot-auth/index.ts +6 -0
  441. package/src/web-bot-auth/internal/JwkThumbprint.ts +21 -0
  442. package/src/web-bot-auth/internal/SignatureAgent.ts +18 -0
  443. package/src/web-bot-auth/server/index.ts +113 -0
  444. package/src/x402/Exact.e2e.test.ts +121 -81
  445. package/src/x402/Exact.localnet.test.ts +1 -1
  446. package/src/x402/server/EvmCharge.ts +35 -19
  447. package/dist/tempo/legacy/server/Session.d.ts +0 -208
  448. package/dist/tempo/legacy/server/Session.d.ts.map +0 -1
  449. package/dist/tempo/legacy/server/Session.js +0 -698
  450. package/dist/tempo/legacy/server/Session.js.map +0 -1
  451. package/dist/tempo/legacy/server/index.d.ts +0 -5
  452. package/dist/tempo/legacy/server/index.d.ts.map +0 -1
  453. package/dist/tempo/legacy/server/index.js +0 -5
  454. package/dist/tempo/legacy/server/index.js.map +0 -1
  455. package/dist/tempo/legacy/session/ChannelStore.d.ts +0 -22
  456. package/dist/tempo/legacy/session/ChannelStore.d.ts.map +0 -1
  457. package/dist/tempo/legacy/session/ChannelStore.js +0 -6
  458. package/dist/tempo/legacy/session/ChannelStore.js.map +0 -1
  459. package/src/tempo/legacy/server/Defaults.test-d.ts +0 -9
  460. package/src/tempo/legacy/server/Session.test.ts +0 -6370
  461. package/src/tempo/legacy/server/Session.ts +0 -1024
  462. package/src/tempo/legacy/server/index.ts +0 -4
  463. package/src/tempo/legacy/session/ChannelStore.test.ts +0 -58
  464. package/src/tempo/legacy/session/ChannelStore.ts +0 -39
@@ -4,6 +4,8 @@ import { serve } from '@hono/node-server'
4
4
  import { Hono } from 'hono'
5
5
  import { Challenge, Constants, Credential } from 'mppx'
6
6
  import { Mppx as Mppx_server, tempo as tempo_server } from 'mppx/server'
7
+ import { Secp256k1 } from 'ox'
8
+ import { TxEnvelopeTempo } from 'ox/tempo'
7
9
  import {
8
10
  type Address,
9
11
  createClient,
@@ -14,6 +16,7 @@ import {
14
16
  encodeFunctionData,
15
17
  encodeFunctionResult,
16
18
  type Hex,
19
+ maxUint256,
17
20
  zeroAddress,
18
21
  } from 'viem'
19
22
  import { privateKeyToAccount } from 'viem/accounts'
@@ -40,9 +43,8 @@ import * as ChannelStore from './ChannelStore.js'
40
43
  import { charge, session, type ResolveSessionChannelId } from './Session.js'
41
44
  import * as TempoWs from './Ws.js'
42
45
 
43
- const payer = privateKeyToAccount(
44
- '0xac0974bec39a17e36ba6a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
45
- )
46
+ const payerPrivateKey = '0xac0974bec39a17e36ba6a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
47
+ const payer = privateKeyToAccount(payerPrivateKey)
46
48
  const wrongPayer = privateKeyToAccount(
47
49
  '0x59c6995e998f97a5a0044966f094538a009d74290f5811cfba6a6b4d238ff944',
48
50
  )
@@ -88,10 +90,16 @@ function createServerClient(
88
90
  _eventChannelId: Hex = `0x${'00'.repeat(32)}` as Hex,
89
91
  options: {
90
92
  descriptor?: Channel.ChannelDescriptor
91
- receipt?: Record<string, unknown>
92
- state?: ChainState
93
+ receipt?:
94
+ | Record<string, unknown>
95
+ | (() => Record<string, unknown> | Promise<Record<string, unknown>>)
96
+ sentReceipt?:
97
+ | Record<string, unknown>
98
+ | (() => Record<string, unknown> | Promise<Record<string, unknown>>)
99
+ state?: ChainState | (() => ChainState | Promise<ChainState>)
93
100
  } = {},
94
101
  ) {
102
+ let sentTransaction = false
95
103
  return createClient({
96
104
  ...(account ? { account } : {}),
97
105
  chain: testChain,
@@ -103,11 +111,24 @@ function createServerClient(
103
111
  if (args.method === 'eth_estimateGas') return '0x5208'
104
112
  if (args.method === 'eth_maxPriorityFeePerGas') return '0x1'
105
113
  if (args.method === 'eth_getBlockByNumber') return { baseFeePerGas: '0x1' }
106
- if (args.method === 'eth_sendRawTransaction') return `0x${'aa'.repeat(32)}`
107
- if (args.method === 'eth_getTransactionReceipt') return options.receipt ?? null
108
- if (args.method === 'eth_sendTransaction') return `0x${'bb'.repeat(32)}`
114
+ if (args.method === 'eth_sendRawTransaction') {
115
+ sentTransaction = true
116
+ return `0x${'aa'.repeat(32)}`
117
+ }
118
+ if (args.method === 'eth_getTransactionReceipt') {
119
+ const receipt = sentTransaction
120
+ ? (options.sentReceipt ?? options.receipt)
121
+ : options.receipt
122
+ return typeof receipt === 'function' ? receipt() : (receipt ?? null)
123
+ }
124
+ if (args.method === 'eth_sendTransaction') {
125
+ sentTransaction = true
126
+ return `0x${'bb'.repeat(32)}`
127
+ }
109
128
  if (args.method === 'eth_call') {
110
- const state = options.state ?? { settled: 100n, deposit: 1_000n, closeRequestedAt: 0 }
129
+ const configuredState =
130
+ typeof options.state === 'function' ? await options.state() : options.state
131
+ const state = configuredState ?? { settled: 100n, deposit: 1_000n, closeRequestedAt: 0 }
111
132
  const data = (args.params as [{ data?: Hex }])[0].data
112
133
  const getChannelSelector = options.descriptor
113
134
  ? encodeFunctionData({
@@ -315,6 +336,52 @@ async function createOpenPayload(
315
336
  }
316
337
  }
317
338
 
339
+ function sponsorTransaction(
340
+ serializedTransaction: Hex,
341
+ options: { gas?: bigint | undefined } = {},
342
+ ): Hex {
343
+ const transaction = Transaction.deserialize(
344
+ serializedTransaction as Transaction.TransactionSerializedTempo,
345
+ )
346
+ const envelope = TxEnvelopeTempo.from({
347
+ ...transaction,
348
+ feePayerSignature: null,
349
+ gas: options.gas ?? 100_000n,
350
+ maxFeePerGas: 1n,
351
+ maxPriorityFeePerGas: 1n,
352
+ nonceKey: maxUint256,
353
+ validBefore: Math.floor(Date.now() / 1_000) + 600,
354
+ } as never)
355
+ return TxEnvelopeTempo.serialize(envelope, {
356
+ signature: Secp256k1.sign({
357
+ payload: TxEnvelopeTempo.getSignPayload(envelope),
358
+ privateKey: payerPrivateKey,
359
+ }),
360
+ }) as Hex
361
+ }
362
+
363
+ async function createSponsoredOpenPayload(): Promise<
364
+ Extract<SessionCredentialPayload, { action: 'open' }>
365
+ > {
366
+ const payload = await createOpenPayload()
367
+ const transaction = sponsorTransaction(payload.transaction)
368
+ const signed = Transaction.deserialize(transaction as Transaction.TransactionSerializedTempo)
369
+ const expiringNonceHash = Channel.computeExpiringNonceHash(
370
+ Channel.transactionForExpiringNonceHash({ feePayer: true, transaction: signed }),
371
+ { sender: payer.address },
372
+ )
373
+ const descriptor = { ...payload.descriptor, expiringNonceHash }
374
+ const channelId = Channel.computeId({ ...descriptor, chainId, escrow: tip20ChannelEscrow })
375
+ const signature = await Voucher.signVoucher(
376
+ createSigningClient(),
377
+ payer,
378
+ { channelId, cumulativeAmount: BigInt(payload.cumulativeAmount) },
379
+ tip20ChannelEscrow,
380
+ chainId,
381
+ )
382
+ return { ...payload, channelId, transaction, signature, descriptor }
383
+ }
384
+
318
385
  function transactionReceipt(logs: readonly Record<string, unknown>[]) {
319
386
  return {
320
387
  blockHash: `0x${'01'.repeat(32)}`,
@@ -757,6 +824,133 @@ describe('precompile server session unit guardrails', () => {
757
824
  ).rejects.toThrow('Client not configured with chainId 1.')
758
825
  })
759
826
 
827
+ test('fully validates a session credential without broadcasting or changing channel state', async () => {
828
+ const { method, rpcCalls, store } = createServer()
829
+ const openPayload = await createOpenPayload()
830
+ await persistPrecompileChannel(store, openPayload)
831
+ const payload = await ClientOps.createVoucherPayload(
832
+ createSigningClient(),
833
+ payer,
834
+ openPayload.descriptor,
835
+ Types.uint96(200n),
836
+ chainId,
837
+ )
838
+
839
+ const validation = await method.validate!({
840
+ credential: {
841
+ challenge: makeChallenge(openPayload.channelId),
842
+ payload,
843
+ source: sourceFor(),
844
+ },
845
+ request: verifyRequest(openPayload.channelId),
846
+ })
847
+
848
+ await method.validate!({
849
+ credential: {
850
+ challenge: makeChallenge(openPayload.channelId),
851
+ payload,
852
+ source: sourceFor(),
853
+ },
854
+ request: verifyRequest(openPayload.channelId),
855
+ })
856
+
857
+ expect(validation.details).toEqual({ action: 'voucher', channelId: openPayload.channelId })
858
+ expect(await store.getChannel(openPayload.channelId)).toMatchObject({
859
+ highestVoucherAmount: 100n,
860
+ spent: 0n,
861
+ units: 0,
862
+ })
863
+ expect(rpcCalls.map(({ method }) => method)).toEqual(['eth_call'])
864
+ expect(rpcCalls.map(({ method }) => method)).not.toContain('eth_sendRawTransaction')
865
+ expect(rpcCalls.map(({ method }) => method)).not.toContain('eth_sendRawTransactionSync')
866
+ })
867
+
868
+ test('simulates unsponsored open and top-up credentials during validation', async () => {
869
+ const { method, rpcCalls, store } = createServer()
870
+ const openPayload = await createOpenPayload()
871
+
872
+ await method.validate!({
873
+ credential: { challenge: makeChallenge(openPayload.channelId), payload: openPayload },
874
+ request: verifyRequest(openPayload.channelId),
875
+ })
876
+ await persistPrecompileChannel(store, openPayload)
877
+ const topUpPayload = await createTopUpPayload(openPayload.descriptor, 100n)
878
+
879
+ await method.validate!({
880
+ credential: { challenge: makeChallenge(topUpPayload.channelId), payload: topUpPayload },
881
+ request: verifyRequest(topUpPayload.channelId),
882
+ })
883
+
884
+ expect(rpcCalls.map(({ method }) => method)).toEqual(['eth_call', 'eth_call', 'eth_call'])
885
+ expect(await store.getChannel(openPayload.channelId)).toMatchObject({
886
+ deposit: 1_000n,
887
+ highestVoucherAmount: 100n,
888
+ })
889
+ })
890
+
891
+ test('does not simulate sponsored credentials during validation but checks top-up state', async () => {
892
+ const { method, rpcCalls, store } = createServer({ feePayer: payer })
893
+ const openPayload = await createSponsoredOpenPayload()
894
+
895
+ await method.validate!({
896
+ credential: { challenge: makeChallenge(openPayload.channelId), payload: openPayload },
897
+ request: verifyRequestWithFeePayer(openPayload.channelId, payer),
898
+ })
899
+ await persistPrecompileChannel(store, openPayload)
900
+ const topUpPayload = await createTopUpPayload(openPayload.descriptor, 100n)
901
+ topUpPayload.transaction = sponsorTransaction(topUpPayload.transaction)
902
+
903
+ await method.validate!({
904
+ credential: { challenge: makeChallenge(topUpPayload.channelId), payload: topUpPayload },
905
+ request: verifyRequestWithFeePayer(topUpPayload.channelId, payer),
906
+ })
907
+
908
+ expect(rpcCalls.map(({ method }) => method)).toEqual(['eth_call'])
909
+ expect(await store.getChannel(openPayload.channelId)).toMatchObject({
910
+ deposit: 1_000n,
911
+ highestVoucherAmount: 100n,
912
+ })
913
+ })
914
+
915
+ test('rejects sponsored credentials that exceed the fee-payer policy during validation', async () => {
916
+ const { method, rpcCalls } = createServer({
917
+ feePayer: payer,
918
+ feePayerPolicy: { maxGas: 1n },
919
+ })
920
+ const payload = await createSponsoredOpenPayload()
921
+
922
+ payload.transaction = sponsorTransaction(payload.transaction, { gas: 2n })
923
+
924
+ await expect(
925
+ method.validate!({
926
+ credential: { challenge: makeChallenge(payload.channelId), payload },
927
+ request: verifyRequestWithFeePayer(payload.channelId, payer),
928
+ }),
929
+ ).rejects.toThrow(/fee-sponsored transaction gas exceeds sponsor policy/)
930
+ expect(rpcCalls).toEqual([])
931
+ })
932
+
933
+ test('validates close credentials without marking the channel pending', async () => {
934
+ const { method, rpcCalls, store } = createServer()
935
+ const openPayload = await createOpenPayload()
936
+ await persistPrecompileChannel(store, openPayload, { payee: payer.address })
937
+ const payload = await ClientOps.createClosePayload(
938
+ createSigningClient(),
939
+ payer,
940
+ openPayload.descriptor,
941
+ Types.uint96(100n),
942
+ chainId,
943
+ )
944
+
945
+ await method.validate!({
946
+ credential: { challenge: makeChallenge(openPayload.channelId), payload },
947
+ request: verifyRequest(openPayload.channelId),
948
+ })
949
+
950
+ expect((await store.getChannel(openPayload.channelId))?.closeRequestedAt).toBe(0n)
951
+ expect(rpcCalls.map(({ method }) => method)).toEqual(['eth_call'])
952
+ })
953
+
760
954
  test('rejects open transactions targeting the wrong address', async () => {
761
955
  const { method } = createServer()
762
956
  const payload = await createOpenPayload({ escrow: wrongTarget })
@@ -1409,7 +1603,7 @@ describe('precompile server session unit guardrails', () => {
1409
1603
  expect(stored?.units).toBe(4)
1410
1604
  })
1411
1605
 
1412
- test('rejects precompile top-up when on-chain state has pending close', async () => {
1606
+ test('rejects top-up validation when on-chain state has pending close', async () => {
1413
1607
  const rawStore = Store.memory()
1414
1608
  const store = channelStore(rawStore)
1415
1609
  const openPayload = await createOpenPayload({ initialAmount: 100n })
@@ -1431,7 +1625,7 @@ describe('precompile server session unit guardrails', () => {
1431
1625
  })
1432
1626
 
1433
1627
  await expect(
1434
- method.verify({
1628
+ method.validate!({
1435
1629
  credential: {
1436
1630
  challenge: makeChallenge(openPayload.channelId),
1437
1631
  payload: topUpPayload,
@@ -2172,14 +2366,23 @@ describe('precompile server session unit guardrails', () => {
2172
2366
 
2173
2367
  describe('SSE parity', () => {
2174
2368
  function createManagedSseFetch(
2175
- options: { amount?: string; maxDeposit?: bigint; unitType?: 'request' | 'token' } = {},
2369
+ options: {
2370
+ amount?: string
2371
+ chunkDelayMs?: number
2372
+ maxDeposit?: bigint
2373
+ onSessionSettlement?: session.Parameters['onSessionSettlement']
2374
+ settlementSchedule?: session.Parameters['settlementSchedule']
2375
+ unitType?: 'request' | 'token'
2376
+ } = {},
2176
2377
  ) {
2177
2378
  const rawStore = Store.memory()
2379
+ let activeChannelId: Hex | undefined
2178
2380
  let currentPayload: SessionCredentialPayload | undefined
2179
2381
  let voucherPosts = 0
2180
2382
  const amount = options.amount ?? '1'
2181
2383
  const maxDeposit = options.maxDeposit ?? 3n
2182
2384
  let deposit = maxDeposit
2385
+ let settled = 0n
2183
2386
  const unitType = options.unitType ?? 'token'
2184
2387
  const route = Mppx_server.create({
2185
2388
  methods: [
@@ -2189,6 +2392,8 @@ describe('precompile server session unit guardrails', () => {
2189
2392
  currency: token,
2190
2393
  decimals: 0,
2191
2394
  recipient: payer.address,
2395
+ onSessionSettlement: options.onSessionSettlement,
2396
+ settlementSchedule: options.settlementSchedule,
2192
2397
  sse: true,
2193
2398
  store: rawStore,
2194
2399
  unitType,
@@ -2206,20 +2411,24 @@ describe('precompile server session unit guardrails', () => {
2206
2411
  receipt: transactionReceipt([
2207
2412
  closedLog(payload.channelId, BigInt(payload.cumulativeAmount), 0n),
2208
2413
  ]),
2209
- state: { settled: 0n, deposit, closeRequestedAt: 0 },
2414
+ state: { settled, deposit, closeRequestedAt: 0 },
2210
2415
  })
2211
2416
  }
2212
2417
  if (payload?.action === 'topUp') {
2213
2418
  deposit += BigInt(payload.additionalDeposit)
2214
2419
  return createServerClient([], payer, payload.channelId, {
2215
2420
  receipt: transactionReceipt([topUpLog(payload, deposit)]),
2216
- state: { settled: 0n, deposit, closeRequestedAt: 0 },
2421
+ state: { settled, deposit, closeRequestedAt: 0 },
2217
2422
  })
2218
2423
  }
2219
- return createStateClient(payer, {
2220
- settled: 0n,
2221
- deposit,
2222
- closeRequestedAt: 0,
2424
+ return createServerClient([], payer, undefined, {
2425
+ sentReceipt: async () => {
2426
+ if (!activeChannelId) throw new Error('missing channel context')
2427
+ const channel = await channelStore(rawStore).getChannel(activeChannelId)
2428
+ settled = channel?.highestVoucherAmount ?? settled
2429
+ return transactionReceipt([settledLog(activeChannelId, settled)])
2430
+ },
2431
+ state: () => ({ settled, deposit, closeRequestedAt: 0 }),
2223
2432
  })
2224
2433
  },
2225
2434
  }),
@@ -2233,6 +2442,7 @@ describe('precompile server session unit guardrails', () => {
2233
2442
  if (request.headers.has('Authorization')) {
2234
2443
  try {
2235
2444
  currentPayload = Credential.fromRequest<SessionCredentialPayload>(request).payload
2445
+ activeChannelId = currentPayload.channelId
2236
2446
  if (currentPayload.action === 'voucher') voucherPosts++
2237
2447
  } catch {}
2238
2448
  }
@@ -2259,9 +2469,12 @@ describe('precompile server session unit guardrails', () => {
2259
2469
  )
2260
2470
  }
2261
2471
 
2472
+ currentPayload = undefined
2262
2473
  return result.withReceipt(async function* (stream) {
2263
2474
  await stream.charge()
2264
2475
  yield 'chunk-1'
2476
+ if (options.chunkDelayMs)
2477
+ await new Promise((resolve) => setTimeout(resolve, options.chunkDelayMs))
2265
2478
  await stream.charge()
2266
2479
  yield 'chunk-2'
2267
2480
  await stream.charge()
@@ -2312,6 +2525,49 @@ describe('precompile server session unit guardrails', () => {
2312
2525
  expect(persisted?.finalized).toBe(true)
2313
2526
  })
2314
2527
 
2528
+ test.each([
2529
+ ['unit', { units: 2 }, 0],
2530
+ ['amount', { amount: '2' }, 0],
2531
+ ['interval', { intervalMs: 500 }, 750],
2532
+ ] as const)(
2533
+ 'applies the %s settlement schedule after SSE charges',
2534
+ async (_label, settlementSchedule, chunkDelayMs) => {
2535
+ const settlements: Array<{ delta: bigint; trigger: string }> = []
2536
+ const harness = createManagedSseFetch({
2537
+ chunkDelayMs,
2538
+ maxDeposit: 3n,
2539
+ settlementSchedule,
2540
+ onSessionSettlement(context) {
2541
+ settlements.push({ delta: context.delta, trigger: context.trigger })
2542
+ },
2543
+ })
2544
+ const manager = precompileSessionManager({
2545
+ account: payer,
2546
+ client: createSigningClient(),
2547
+ decimals: 0,
2548
+ fetch: harness.fetch,
2549
+ maxDeposit: '3',
2550
+ })
2551
+
2552
+ const chunks: string[] = []
2553
+ for await (const chunk of await manager.sse('https://api.example.com/stream')) {
2554
+ chunks.push(chunk)
2555
+ }
2556
+
2557
+ expect(chunks).toEqual(['chunk-1', 'chunk-2', 'chunk-3'])
2558
+ expect(harness.voucherPosts).toBeGreaterThan(0)
2559
+ expect(settlements).toEqual([{ delta: 2n, trigger: 'scheduled' }])
2560
+ const persisted = await channelStore(harness.rawStore).getChannel(manager.channelId!)
2561
+ expect(persisted?.lastSettlementUnits).toBe(2)
2562
+
2563
+ await manager.close()
2564
+ expect(settlements).toEqual([
2565
+ { delta: 2n, trigger: 'scheduled' },
2566
+ { delta: 1n, trigger: 'close' },
2567
+ ])
2568
+ },
2569
+ )
2570
+
2315
2571
  test('handles empty management POST streams through a Node adapter', async () => {
2316
2572
  const harness = createManagedSseFetch({ maxDeposit: 1n })
2317
2573
  const managementPosts: Array<{
@@ -2422,11 +2678,18 @@ describe('precompile server session unit guardrails', () => {
2422
2678
 
2423
2679
  describe('WebSocket parity', () => {
2424
2680
  async function createManagedWsHarness(
2425
- options: { challengeTtlMs?: number; maxDeposit?: bigint } = {},
2681
+ options: {
2682
+ challengeTtlMs?: number
2683
+ maxDeposit?: bigint
2684
+ onSessionSettlement?: session.Parameters['onSessionSettlement']
2685
+ settlementSchedule?: session.Parameters['settlementSchedule']
2686
+ } = {},
2426
2687
  ) {
2427
2688
  const rawStore = Store.memory()
2689
+ let activeChannelId: Hex | undefined
2428
2690
  let challengeRequests = 0
2429
2691
  let currentPayload: SessionCredentialPayload | undefined
2692
+ let settled = 0n
2430
2693
  let voucherPosts = 0
2431
2694
  const maxDeposit = options.maxDeposit ?? 3n
2432
2695
  const payment = Mppx_server.create({
@@ -2436,7 +2699,9 @@ describe('precompile server session unit guardrails', () => {
2436
2699
  chainId,
2437
2700
  currency: token,
2438
2701
  decimals: 0,
2702
+ onSessionSettlement: options.onSessionSettlement,
2439
2703
  recipient: payer.address,
2704
+ settlementSchedule: options.settlementSchedule,
2440
2705
  store: rawStore,
2441
2706
  unitType: 'token',
2442
2707
  getClient: () => {
@@ -2453,13 +2718,17 @@ describe('precompile server session unit guardrails', () => {
2453
2718
  receipt: transactionReceipt([
2454
2719
  closedLog(payload.channelId, BigInt(payload.cumulativeAmount), 0n),
2455
2720
  ]),
2456
- state: { settled: 0n, deposit: maxDeposit, closeRequestedAt: 0 },
2721
+ state: { settled, deposit: maxDeposit, closeRequestedAt: 0 },
2457
2722
  })
2458
2723
  }
2459
- return createStateClient(payer, {
2460
- settled: 0n,
2461
- deposit: maxDeposit,
2462
- closeRequestedAt: 0,
2724
+ return createServerClient([], payer, undefined, {
2725
+ sentReceipt: async () => {
2726
+ if (!activeChannelId) throw new Error('missing channel context')
2727
+ const channel = await channelStore(rawStore).getChannel(activeChannelId)
2728
+ settled = channel?.highestVoucherAmount ?? settled
2729
+ return transactionReceipt([settledLog(activeChannelId, settled)])
2730
+ },
2731
+ state: () => ({ settled, deposit: maxDeposit, closeRequestedAt: 0 }),
2463
2732
  })
2464
2733
  },
2465
2734
  }),
@@ -2468,23 +2737,26 @@ describe('precompile server session unit guardrails', () => {
2468
2737
  secretKey: 'test-secret-key-test-secret-key-32',
2469
2738
  })
2470
2739
 
2740
+ const routeOptions = () => ({
2741
+ amount: '1',
2742
+ decimals: 0,
2743
+ suggestedDeposit: maxDeposit.toString(),
2744
+ ...(options.challengeTtlMs === undefined
2745
+ ? {}
2746
+ : { expires: new Date(Date.now() + options.challengeTtlMs) }),
2747
+ })
2471
2748
  const route = async (request: Request) => {
2472
2749
  currentPayload = undefined
2473
2750
  if (request.headers.has('Authorization')) {
2474
2751
  try {
2475
2752
  currentPayload = Credential.fromRequest<SessionCredentialPayload>(request).payload
2753
+ activeChannelId = currentPayload.channelId
2476
2754
  if (currentPayload.action === 'voucher') voucherPosts++
2477
2755
  } catch {}
2478
2756
  } else challengeRequests++
2479
- const routeHandler = payment.session({
2480
- amount: '1',
2481
- decimals: 0,
2482
- suggestedDeposit: maxDeposit.toString(),
2483
- ...(options.challengeTtlMs === undefined
2484
- ? {}
2485
- : { expires: new Date(Date.now() + options.challengeTtlMs) }),
2486
- })
2487
- return routeHandler(request)
2757
+ const result = await payment.session(routeOptions())(request)
2758
+ currentPayload = undefined
2759
+ return result
2488
2760
  }
2489
2761
 
2490
2762
  const httpHandler = NodeRequest.toNodeListener(async (request) => {
@@ -2516,6 +2788,7 @@ describe('precompile server session unit guardrails', () => {
2516
2788
  return {
2517
2789
  rawStore,
2518
2790
  route,
2791
+ serveWebSocket: payment.session.serveWebSocket,
2519
2792
  server,
2520
2793
  wsServer,
2521
2794
  get port() {
@@ -2537,9 +2810,8 @@ describe('precompile server session unit guardrails', () => {
2537
2810
  test('refreshes an expired challenge before sending a later websocket voucher', async () => {
2538
2811
  const harness = await createManagedWsHarness({ challengeTtlMs: 2_000, maxDeposit: 2n })
2539
2812
  harness.wsServer.on('connection', (socket: import('ws').WebSocket) => {
2540
- void TempoWs.serve({
2813
+ void harness.serveWebSocket({
2541
2814
  socket,
2542
- store: harness.rawStore,
2543
2815
  url: `${harness.server.url}/ws`,
2544
2816
  route: harness.route,
2545
2817
  generate: async function* (stream: TempoWs.SessionController) {
@@ -2585,9 +2857,8 @@ describe('precompile server session unit guardrails', () => {
2585
2857
  test('open -> stream -> need-voucher -> resume -> close', async () => {
2586
2858
  const harness = await createManagedWsHarness({ maxDeposit: 3n })
2587
2859
  harness.wsServer.on('connection', (socket: import('ws').WebSocket) => {
2588
- void TempoWs.serve({
2860
+ void harness.serveWebSocket({
2589
2861
  socket,
2590
- store: harness.rawStore,
2591
2862
  url: `${harness.server.url}/ws`,
2592
2863
  route: harness.route,
2593
2864
  generate: async function* (stream: TempoWs.SessionController) {
@@ -2641,6 +2912,75 @@ describe('precompile server session unit guardrails', () => {
2641
2912
  }
2642
2913
  })
2643
2914
 
2915
+ test.each([
2916
+ ['unit', { units: 2 }, 0],
2917
+ ['amount', { amount: '2' }, 0],
2918
+ ['interval', { intervalMs: 500 }, 750],
2919
+ ] as const)(
2920
+ 'applies the %s settlement schedule after websocket charges',
2921
+ async (_label, settlementSchedule, chunkDelayMs) => {
2922
+ const settlements: Array<{ delta: bigint; trigger: string }> = []
2923
+ const harness = await createManagedWsHarness({
2924
+ maxDeposit: 3n,
2925
+ settlementSchedule,
2926
+ onSessionSettlement(context) {
2927
+ settlements.push({ delta: context.delta, trigger: context.trigger })
2928
+ },
2929
+ })
2930
+ harness.wsServer.on('connection', (socket: import('ws').WebSocket) => {
2931
+ void harness.serveWebSocket({
2932
+ socket,
2933
+ url: `${harness.server.url}/ws`,
2934
+ route: harness.route,
2935
+ generate: async function* (stream: TempoWs.SessionController) {
2936
+ for (const chunk of ['chunk-1', 'chunk-2', 'chunk-3']) {
2937
+ if (chunk === 'chunk-2' && chunkDelayMs)
2938
+ await new Promise((resolve) => setTimeout(resolve, chunkDelayMs))
2939
+ await stream.charge()
2940
+ yield chunk
2941
+ }
2942
+ },
2943
+ })
2944
+ })
2945
+
2946
+ try {
2947
+ const manager = precompileSessionManager({
2948
+ account: payer,
2949
+ client: createSigningClient(),
2950
+ decimals: 0,
2951
+ fetch: globalThis.fetch,
2952
+ maxDeposit: '3',
2953
+ webSocket: WebSocket as never,
2954
+ })
2955
+ const ws = await manager.ws(`ws://localhost:${harness.port}/ws`)
2956
+ const chunks: string[] = []
2957
+
2958
+ await new Promise<void>((resolve, reject) => {
2959
+ ws.addEventListener('message', (event) => {
2960
+ if (typeof event.data === 'string') chunks.push(event.data)
2961
+ })
2962
+ ws.addEventListener('close', () => resolve(), { once: true })
2963
+ ws.addEventListener('error', () => reject(new Error('websocket stream failed')), {
2964
+ once: true,
2965
+ })
2966
+ })
2967
+
2968
+ expect(chunks).toEqual(['chunk-1', 'chunk-2', 'chunk-3'])
2969
+ expect(harness.voucherPosts).toBeGreaterThan(0)
2970
+ expect(settlements).toEqual([{ delta: 2n, trigger: 'scheduled' }])
2971
+ await manager.close()
2972
+ expect(settlements).toEqual([
2973
+ { delta: 2n, trigger: 'scheduled' },
2974
+ { delta: 1n, trigger: 'close' },
2975
+ ])
2976
+ const persisted = await channelStore(harness.rawStore).getChannel(manager.channelId!)
2977
+ expect(persisted?.lastSettlementUnits).toBe(2)
2978
+ } finally {
2979
+ harness.close()
2980
+ }
2981
+ },
2982
+ )
2983
+
2644
2984
  test('treats control-shaped application payloads as content', async () => {
2645
2985
  const harness = await createManagedWsHarness({ maxDeposit: 1n })
2646
2986
  const controlLookingChunk = JSON.stringify({
@@ -2653,9 +2993,8 @@ describe('precompile server session unit guardrails', () => {
2653
2993
  },
2654
2994
  })
2655
2995
  harness.wsServer.on('connection', (socket: import('ws').WebSocket) => {
2656
- void TempoWs.serve({
2996
+ void harness.serveWebSocket({
2657
2997
  socket,
2658
- store: harness.rawStore,
2659
2998
  url: `${harness.server.url}/ws`,
2660
2999
  route: harness.route,
2661
3000
  generate: async function* (stream: TempoWs.SessionController) {
@@ -2703,9 +3042,8 @@ describe('precompile server session unit guardrails', () => {
2703
3042
  test('refuses websocket voucher requests beyond local maxDeposit', async () => {
2704
3043
  const harness = await createManagedWsHarness({ maxDeposit: 1n })
2705
3044
  harness.wsServer.on('connection', (socket: import('ws').WebSocket) => {
2706
- void TempoWs.serve({
3045
+ void harness.serveWebSocket({
2707
3046
  socket,
2708
- store: harness.rawStore,
2709
3047
  url: `${harness.server.url}/ws`,
2710
3048
  route: harness.route,
2711
3049
  generate: async function* (stream: TempoWs.SessionController) {
@@ -2865,9 +3203,8 @@ describe('precompile server session unit guardrails', () => {
2865
3203
  let serverSocket: import('ws').WebSocket | null = null
2866
3204
  harness.wsServer.on('connection', (socket: import('ws').WebSocket) => {
2867
3205
  serverSocket = socket
2868
- void TempoWs.serve({
3206
+ void harness.serveWebSocket({
2869
3207
  socket,
2870
- store: harness.rawStore,
2871
3208
  url: `${harness.server.url}/ws`,
2872
3209
  route: harness.route,
2873
3210
  generate: async function* (stream: TempoWs.SessionController) {
@@ -3461,6 +3798,54 @@ describe('precompile server session unit guardrails', () => {
3461
3798
  })
3462
3799
  })
3463
3800
 
3801
+ describe('session settlement extensions', () => {
3802
+ test.each([
3803
+ ['no schedule is configured', undefined],
3804
+ ['the configured threshold is not due', { units: 11 }],
3805
+ ] as const)('does not resolve a client when %s', async (_label, settlementSchedule) => {
3806
+ const rawStore = Store.memory()
3807
+ const store = channelStore(rawStore)
3808
+ const openPayload = await createOpenPayload()
3809
+ let clientResolutions = 0
3810
+ await persistPrecompileChannel(store, openPayload, {
3811
+ highestVoucherAmount: 500n,
3812
+ highestVoucher: {
3813
+ channelId: openPayload.channelId,
3814
+ cumulativeAmount: 500n,
3815
+ signature: '0x1234',
3816
+ },
3817
+ spent: 500n,
3818
+ units: 10,
3819
+ })
3820
+
3821
+ const payment = Mppx_server.create({
3822
+ methods: [
3823
+ tempo_server.session({
3824
+ account: payer,
3825
+ amount: '1',
3826
+ chainId,
3827
+ currency: token,
3828
+ decimals: 0,
3829
+ getClient() {
3830
+ clientResolutions++
3831
+ throw new Error('client should not be resolved')
3832
+ },
3833
+ recipient: payee,
3834
+ settlementSchedule,
3835
+ store: rawStore,
3836
+ unitType: 'token',
3837
+ }),
3838
+ ],
3839
+ realm: 'api.example.com',
3840
+ secretKey: 'test-secret-key-test-secret-key-32',
3841
+ })
3842
+ const channel = await store.getChannel(openPayload.channelId)
3843
+
3844
+ await expect(payment.session.settleScheduled(channel!)).resolves.toBeUndefined()
3845
+ expect(clientResolutions).toBe(0)
3846
+ })
3847
+ })
3848
+
3464
3849
  describe('onSessionSettlement', () => {
3465
3850
  function createSettleClient(channelId: Hex, settledAmount: bigint) {
3466
3851
  return createClient({