mppx 0.6.30 → 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 (483) hide show
  1. package/CHANGELOG.md +25 -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/Request.js +24 -10
  62. package/dist/server/Request.js.map +1 -1
  63. package/dist/server/Response.d.ts.map +1 -1
  64. package/dist/server/Response.js +2 -1
  65. package/dist/server/Response.js.map +1 -1
  66. package/dist/server/Transport.d.ts.map +1 -1
  67. package/dist/server/Transport.js +4 -3
  68. package/dist/server/Transport.js.map +1 -1
  69. package/dist/server/index.d.ts +1 -0
  70. package/dist/server/index.d.ts.map +1 -1
  71. package/dist/server/index.js +1 -0
  72. package/dist/server/index.js.map +1 -1
  73. package/dist/stripe/client/Charge.d.ts +1 -1
  74. package/dist/stripe/client/Charge.d.ts.map +1 -1
  75. package/dist/stripe/client/Charge.js +3 -1
  76. package/dist/stripe/client/Charge.js.map +1 -1
  77. package/dist/stripe/server/Charge.d.ts +1 -1
  78. package/dist/stripe/server/Charge.d.ts.map +1 -1
  79. package/dist/stripe/server/Charge.js +9 -2
  80. package/dist/stripe/server/Charge.js.map +1 -1
  81. package/dist/stripe/server/Methods.d.ts +1 -1
  82. package/dist/stripe/server/Methods.d.ts.map +1 -1
  83. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  84. package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
  85. package/dist/stripe/server/internal/html.gen.js +1 -1
  86. package/dist/stripe/server/internal/html.gen.js.map +1 -1
  87. package/dist/tempo/Methods.d.ts +18 -0
  88. package/dist/tempo/Methods.d.ts.map +1 -1
  89. package/dist/tempo/Methods.js +16 -1
  90. package/dist/tempo/Methods.js.map +1 -1
  91. package/dist/tempo/client/Charge.d.ts +6 -0
  92. package/dist/tempo/client/Charge.d.ts.map +1 -1
  93. package/dist/tempo/client/Charge.js +9 -2
  94. package/dist/tempo/client/Charge.js.map +1 -1
  95. package/dist/tempo/client/Methods.d.ts +36 -7
  96. package/dist/tempo/client/Methods.d.ts.map +1 -1
  97. package/dist/tempo/client/Methods.js +12 -5
  98. package/dist/tempo/client/Methods.js.map +1 -1
  99. package/dist/tempo/client/index.d.ts +7 -4
  100. package/dist/tempo/client/index.d.ts.map +1 -1
  101. package/dist/tempo/client/index.js +5 -3
  102. package/dist/tempo/client/index.js.map +1 -1
  103. package/dist/tempo/index.d.ts +1 -0
  104. package/dist/tempo/index.d.ts.map +1 -1
  105. package/dist/tempo/index.js +1 -0
  106. package/dist/tempo/index.js.map +1 -1
  107. package/dist/tempo/internal/fee-payer.d.ts +21 -1
  108. package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
  109. package/dist/tempo/internal/fee-payer.js +109 -4
  110. package/dist/tempo/internal/fee-payer.js.map +1 -1
  111. package/dist/tempo/{client → legacy/client}/ChannelOps.d.ts +19 -6
  112. package/dist/tempo/legacy/client/ChannelOps.d.ts.map +1 -0
  113. package/dist/tempo/{client → legacy/client}/ChannelOps.js +9 -3
  114. package/dist/tempo/legacy/client/ChannelOps.js.map +1 -0
  115. package/dist/tempo/{client → legacy/client}/Session.d.ts +23 -4
  116. package/dist/tempo/legacy/client/Session.d.ts.map +1 -0
  117. package/dist/tempo/{client → legacy/client}/Session.js +14 -7
  118. package/dist/tempo/legacy/client/Session.js.map +1 -0
  119. package/dist/tempo/{client → legacy/client}/SessionManager.d.ts +20 -5
  120. package/dist/tempo/legacy/client/SessionManager.d.ts.map +1 -0
  121. package/dist/tempo/{client → legacy/client}/SessionManager.js +20 -16
  122. package/dist/tempo/legacy/client/SessionManager.js.map +1 -0
  123. package/dist/tempo/legacy/client/index.d.ts +7 -0
  124. package/dist/tempo/legacy/client/index.d.ts.map +1 -0
  125. package/dist/tempo/legacy/client/index.js +5 -0
  126. package/dist/tempo/legacy/client/index.js.map +1 -0
  127. package/dist/tempo/legacy/index.d.ts +7 -0
  128. package/dist/tempo/legacy/index.d.ts.map +1 -0
  129. package/dist/tempo/legacy/index.js +7 -0
  130. package/dist/tempo/legacy/index.js.map +1 -0
  131. package/dist/tempo/{server → legacy/server}/Session.d.ts +28 -11
  132. package/dist/tempo/legacy/server/Session.d.ts.map +1 -0
  133. package/dist/tempo/{server → legacy/server}/Session.js +28 -23
  134. package/dist/tempo/legacy/server/Session.js.map +1 -0
  135. package/dist/tempo/legacy/server/index.d.ts +5 -0
  136. package/dist/tempo/legacy/server/index.d.ts.map +1 -0
  137. package/dist/tempo/legacy/server/index.js +5 -0
  138. package/dist/tempo/legacy/server/index.js.map +1 -0
  139. package/dist/tempo/{session → legacy/session}/Chain.d.ts +30 -23
  140. package/dist/tempo/legacy/session/Chain.d.ts.map +1 -0
  141. package/dist/tempo/{session → legacy/session}/Chain.js +12 -11
  142. package/dist/tempo/legacy/session/Chain.js.map +1 -0
  143. package/dist/tempo/{session → legacy/session}/Channel.d.ts +1 -0
  144. package/dist/tempo/legacy/session/Channel.d.ts.map +1 -0
  145. package/dist/tempo/legacy/session/Channel.js.map +1 -0
  146. package/dist/tempo/legacy/session/ChannelStore.d.ts +22 -0
  147. package/dist/tempo/legacy/session/ChannelStore.d.ts.map +1 -0
  148. package/dist/tempo/legacy/session/ChannelStore.js +6 -0
  149. package/dist/tempo/legacy/session/ChannelStore.js.map +1 -0
  150. package/dist/tempo/legacy/session/Types.d.ts +73 -0
  151. package/dist/tempo/legacy/session/Types.d.ts.map +1 -0
  152. package/dist/tempo/legacy/session/Types.js.map +1 -0
  153. package/dist/tempo/{session → legacy/session}/Voucher.d.ts +4 -4
  154. package/dist/tempo/legacy/session/Voucher.d.ts.map +1 -0
  155. package/dist/tempo/{session → legacy/session}/Voucher.js +1 -1
  156. package/dist/tempo/legacy/session/Voucher.js.map +1 -0
  157. package/dist/tempo/{session → legacy/session}/escrow.abi.d.ts +1 -0
  158. package/dist/tempo/{session → legacy/session}/escrow.abi.d.ts.map +1 -1
  159. package/dist/tempo/{session → legacy/session}/escrow.abi.js +1 -0
  160. package/dist/tempo/legacy/session/escrow.abi.js.map +1 -0
  161. package/dist/tempo/legacy/session/index.d.ts +9 -0
  162. package/dist/tempo/legacy/session/index.d.ts.map +1 -0
  163. package/dist/tempo/legacy/session/index.js +9 -0
  164. package/dist/tempo/legacy/session/index.js.map +1 -0
  165. package/dist/tempo/server/Charge.d.ts +1 -1
  166. package/dist/tempo/server/Charge.d.ts.map +1 -1
  167. package/dist/tempo/server/Charge.js +13 -16
  168. package/dist/tempo/server/Charge.js.map +1 -1
  169. package/dist/tempo/server/Methods.d.ts +63 -6
  170. package/dist/tempo/server/Methods.d.ts.map +1 -1
  171. package/dist/tempo/server/Methods.js +36 -8
  172. package/dist/tempo/server/Methods.js.map +1 -1
  173. package/dist/tempo/server/Subscription.d.ts +1 -1
  174. package/dist/tempo/server/Subscription.d.ts.map +1 -1
  175. package/dist/tempo/server/index.d.ts +6 -5
  176. package/dist/tempo/server/index.d.ts.map +1 -1
  177. package/dist/tempo/server/index.js +5 -5
  178. package/dist/tempo/server/index.js.map +1 -1
  179. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  180. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
  181. package/dist/tempo/server/internal/html.gen.js +1 -1
  182. package/dist/tempo/server/internal/html.gen.js.map +1 -1
  183. package/dist/tempo/server/internal/request-body.d.ts +7 -2
  184. package/dist/tempo/server/internal/request-body.d.ts.map +1 -1
  185. package/dist/tempo/server/internal/request-body.js +20 -3
  186. package/dist/tempo/server/internal/request-body.js.map +1 -1
  187. package/dist/tempo/server/internal/transport.d.ts +8 -4
  188. package/dist/tempo/server/internal/transport.d.ts.map +1 -1
  189. package/dist/tempo/server/internal/transport.js +8 -7
  190. package/dist/tempo/server/internal/transport.js.map +1 -1
  191. package/dist/tempo/session/Snapshot.d.ts +32 -0
  192. package/dist/tempo/session/Snapshot.d.ts.map +1 -0
  193. package/dist/tempo/session/Snapshot.js +37 -0
  194. package/dist/tempo/session/Snapshot.js.map +1 -0
  195. package/dist/tempo/session/client/ChannelOps.d.ts +82 -0
  196. package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -0
  197. package/dist/tempo/session/client/ChannelOps.js +204 -0
  198. package/dist/tempo/session/client/ChannelOps.js.map +1 -0
  199. package/dist/tempo/session/client/CredentialState.d.ts +262 -0
  200. package/dist/tempo/session/client/CredentialState.d.ts.map +1 -0
  201. package/dist/tempo/session/client/CredentialState.js +417 -0
  202. package/dist/tempo/session/client/CredentialState.js.map +1 -0
  203. package/dist/tempo/session/client/ReceiptCoordinator.d.ts +26 -0
  204. package/dist/tempo/session/client/ReceiptCoordinator.d.ts.map +1 -0
  205. package/dist/tempo/session/client/ReceiptCoordinator.js +61 -0
  206. package/dist/tempo/session/client/ReceiptCoordinator.js.map +1 -0
  207. package/dist/tempo/session/client/Runtime.d.ts +464 -0
  208. package/dist/tempo/session/client/Runtime.d.ts.map +1 -0
  209. package/dist/tempo/session/client/Runtime.js +499 -0
  210. package/dist/tempo/session/client/Runtime.js.map +1 -0
  211. package/dist/tempo/session/client/Session.d.ts +132 -0
  212. package/dist/tempo/session/client/Session.d.ts.map +1 -0
  213. package/dist/tempo/session/client/Session.js +55 -0
  214. package/dist/tempo/session/client/Session.js.map +1 -0
  215. package/dist/tempo/session/client/SessionManager.d.ts +120 -0
  216. package/dist/tempo/session/client/SessionManager.d.ts.map +1 -0
  217. package/dist/tempo/session/client/SessionManager.js +627 -0
  218. package/dist/tempo/session/client/SessionManager.js.map +1 -0
  219. package/dist/tempo/session/client/Transports.d.ts +449 -0
  220. package/dist/tempo/session/client/Transports.d.ts.map +1 -0
  221. package/dist/tempo/session/client/Transports.js +721 -0
  222. package/dist/tempo/session/client/Transports.js.map +1 -0
  223. package/dist/tempo/session/client/index.d.ts +12 -0
  224. package/dist/tempo/session/client/index.d.ts.map +1 -0
  225. package/dist/tempo/session/client/index.js +5 -0
  226. package/dist/tempo/session/client/index.js.map +1 -0
  227. package/dist/tempo/session/index.d.ts +7 -8
  228. package/dist/tempo/session/index.d.ts.map +1 -1
  229. package/dist/tempo/session/index.js +7 -8
  230. package/dist/tempo/session/index.js.map +1 -1
  231. package/dist/tempo/session/precompile/Chain.d.ts +319 -0
  232. package/dist/tempo/session/precompile/Chain.d.ts.map +1 -0
  233. package/dist/tempo/session/precompile/Chain.js +492 -0
  234. package/dist/tempo/session/precompile/Chain.js.map +1 -0
  235. package/dist/tempo/session/precompile/Channel.d.ts +46 -0
  236. package/dist/tempo/session/precompile/Channel.d.ts.map +1 -0
  237. package/dist/tempo/session/precompile/Channel.js +56 -0
  238. package/dist/tempo/session/precompile/Channel.js.map +1 -0
  239. package/dist/tempo/session/precompile/Protocol.d.ts +308 -0
  240. package/dist/tempo/session/precompile/Protocol.d.ts.map +1 -0
  241. package/dist/tempo/session/precompile/Protocol.js +264 -0
  242. package/dist/tempo/session/precompile/Protocol.js.map +1 -0
  243. package/dist/tempo/session/precompile/Voucher.d.ts +40 -0
  244. package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -0
  245. package/dist/tempo/session/precompile/Voucher.js +126 -0
  246. package/dist/tempo/session/precompile/Voucher.js.map +1 -0
  247. package/dist/tempo/session/precompile/escrow.abi.d.ts +522 -0
  248. package/dist/tempo/session/precompile/escrow.abi.d.ts.map +1 -0
  249. package/dist/tempo/session/precompile/escrow.abi.js +224 -0
  250. package/dist/tempo/session/precompile/escrow.abi.js.map +1 -0
  251. package/dist/tempo/session/precompile/index.d.ts +24 -0
  252. package/dist/tempo/session/precompile/index.d.ts.map +1 -0
  253. package/dist/tempo/session/precompile/index.js +22 -0
  254. package/dist/tempo/session/precompile/index.js.map +1 -0
  255. package/dist/tempo/session/server/ChannelOps.d.ts +56 -0
  256. package/dist/tempo/session/server/ChannelOps.d.ts.map +1 -0
  257. package/dist/tempo/session/server/ChannelOps.js +91 -0
  258. package/dist/tempo/session/server/ChannelOps.js.map +1 -0
  259. package/dist/tempo/session/server/ChannelStore.d.ts +347 -0
  260. package/dist/tempo/session/server/ChannelStore.d.ts.map +1 -0
  261. package/dist/tempo/session/server/ChannelStore.js +404 -0
  262. package/dist/tempo/session/server/ChannelStore.js.map +1 -0
  263. package/dist/tempo/session/server/CredentialVerification.d.ts +85 -0
  264. package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -0
  265. package/dist/tempo/session/server/CredentialVerification.js +494 -0
  266. package/dist/tempo/session/server/CredentialVerification.js.map +1 -0
  267. package/dist/tempo/session/server/MeteredStream.d.ts +40 -0
  268. package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -0
  269. package/dist/tempo/session/server/MeteredStream.js +42 -0
  270. package/dist/tempo/session/server/MeteredStream.js.map +1 -0
  271. package/dist/tempo/session/server/RequestState.d.ts +208 -0
  272. package/dist/tempo/session/server/RequestState.d.ts.map +1 -0
  273. package/dist/tempo/session/server/RequestState.js +252 -0
  274. package/dist/tempo/session/server/RequestState.js.map +1 -0
  275. package/dist/tempo/session/server/Session.d.ts +169 -0
  276. package/dist/tempo/session/server/Session.d.ts.map +1 -0
  277. package/dist/tempo/session/server/Session.js +351 -0
  278. package/dist/tempo/session/server/Session.js.map +1 -0
  279. package/dist/tempo/session/server/Settlement.d.ts +185 -0
  280. package/dist/tempo/session/server/Settlement.d.ts.map +1 -0
  281. package/dist/tempo/session/server/Settlement.js +250 -0
  282. package/dist/tempo/session/server/Settlement.js.map +1 -0
  283. package/dist/tempo/session/{Sse.d.ts → server/Sse.d.ts} +9 -56
  284. package/dist/tempo/session/server/Sse.d.ts.map +1 -0
  285. package/dist/tempo/session/server/Sse.js +184 -0
  286. package/dist/tempo/session/server/Sse.js.map +1 -0
  287. package/dist/tempo/session/server/Transports.d.ts +89 -0
  288. package/dist/tempo/session/server/Transports.d.ts.map +1 -0
  289. package/dist/tempo/session/server/Transports.js +149 -0
  290. package/dist/tempo/session/server/Transports.js.map +1 -0
  291. package/dist/tempo/session/server/Ws.d.ts +48 -0
  292. package/dist/tempo/session/server/Ws.d.ts.map +1 -0
  293. package/dist/tempo/session/server/Ws.js +244 -0
  294. package/dist/tempo/session/server/Ws.js.map +1 -0
  295. package/dist/tempo/session/server/index.d.ts +4 -0
  296. package/dist/tempo/session/server/index.d.ts.map +1 -0
  297. package/dist/tempo/session/server/index.js +2 -0
  298. package/dist/tempo/session/server/index.js.map +1 -0
  299. package/package.json +8 -3
  300. package/src/Challenge.ts +9 -7
  301. package/src/Constants.ts +58 -0
  302. package/src/Credential.ts +5 -4
  303. package/src/Method.ts +46 -5
  304. package/src/Receipt.ts +3 -2
  305. package/src/cli/cli.test.ts +23 -28
  306. package/src/cli/cli.ts +23 -10
  307. package/src/cli/mcp.test.ts +21 -7
  308. package/src/cli/plugins/tempo.ts +21 -8
  309. package/src/cli/utils.test.ts +25 -1
  310. package/src/cli/utils.ts +10 -0
  311. package/src/client/Methods.ts +5 -2
  312. package/src/client/Mppx.test-d.ts +10 -0
  313. package/src/client/Mppx.test.ts +75 -0
  314. package/src/client/Transport.ts +4 -5
  315. package/src/client/index.ts +11 -1
  316. package/src/client/internal/Fetch.test.ts +29 -4
  317. package/src/client/internal/Fetch.ts +17 -5
  318. package/src/env.d.ts +1 -1
  319. package/src/index.ts +1 -0
  320. package/src/internal/AcceptPayment.test.ts +61 -0
  321. package/src/internal/AcceptPayment.ts +21 -14
  322. package/src/mcp-sdk/client/McpClient.integration.test.ts +8 -7
  323. package/src/mcp-sdk/client/McpClient.test-d.ts +7 -0
  324. package/src/mcp-sdk/client/McpClient.ts +99 -67
  325. package/src/mcp-sdk/client/McpClient.unit.test.ts +131 -0
  326. package/src/middlewares/elysia.test.ts +8 -4
  327. package/src/middlewares/express.test.ts +8 -4
  328. package/src/middlewares/hono.test.ts +4 -4
  329. package/src/middlewares/nextjs.test.ts +8 -4
  330. package/src/proxy/Proxy.test.ts +8 -8
  331. package/src/server/Mppx.test-d.ts +54 -0
  332. package/src/server/Mppx.test.ts +274 -7
  333. package/src/server/Mppx.ts +487 -406
  334. package/src/server/Request.test.ts +81 -0
  335. package/src/server/Request.ts +23 -9
  336. package/src/server/Response.ts +2 -1
  337. package/src/server/Transport.ts +4 -3
  338. package/src/server/index.ts +1 -0
  339. package/src/stripe/client/Charge.test.ts +20 -5
  340. package/src/stripe/client/Charge.ts +6 -2
  341. package/src/stripe/server/Charge.test.ts +114 -1
  342. package/src/stripe/server/Charge.ts +13 -2
  343. package/src/stripe/server/internal/html/package.json +1 -1
  344. package/src/stripe/server/internal/html.gen.ts +1 -1
  345. package/src/tempo/AccessKeyAuthorization.test.ts +4 -94
  346. package/src/tempo/Methods.test.ts +45 -17
  347. package/src/tempo/Methods.ts +22 -0
  348. package/src/tempo/PublicExports.test-d.ts +105 -0
  349. package/src/tempo/client/Charge.test.ts +85 -0
  350. package/src/tempo/client/Charge.ts +19 -2
  351. package/src/tempo/client/Methods.ts +18 -6
  352. package/src/tempo/client/index.ts +15 -4
  353. package/src/tempo/index.ts +1 -0
  354. package/src/tempo/internal/fee-payer.test.ts +241 -17
  355. package/src/tempo/internal/fee-payer.ts +150 -4
  356. package/src/tempo/internal/fee-token.test.ts +14 -9
  357. package/src/tempo/legacy/AccessKeyAuthorization.test.ts +162 -0
  358. package/src/tempo/legacy/README.md +9 -0
  359. package/src/tempo/{client → legacy/client}/ChannelOps.test.ts +6 -7
  360. package/src/tempo/{client → legacy/client}/ChannelOps.ts +22 -9
  361. package/src/tempo/{client → legacy/client}/Session.test.ts +51 -9
  362. package/src/tempo/{client → legacy/client}/Session.ts +25 -11
  363. package/src/tempo/{client → legacy/client}/SessionManager.test.ts +81 -9
  364. package/src/tempo/{client → legacy/client}/SessionManager.ts +41 -20
  365. package/src/tempo/legacy/client/index.ts +6 -0
  366. package/src/tempo/legacy/index.ts +6 -0
  367. package/src/tempo/{server → legacy/server}/Session.test.ts +162 -63
  368. package/src/tempo/{server → legacy/server}/Session.ts +49 -37
  369. package/src/tempo/legacy/server/index.ts +4 -0
  370. package/src/tempo/{session → legacy/session}/Chain.test.ts +3 -4
  371. package/src/tempo/{session → legacy/session}/Chain.ts +94 -63
  372. package/src/tempo/{session → legacy/session}/Channel.ts +1 -0
  373. package/src/tempo/legacy/session/ChannelStore.test.ts +58 -0
  374. package/src/tempo/legacy/session/ChannelStore.ts +39 -0
  375. package/src/tempo/legacy/session/Types.ts +91 -0
  376. package/src/tempo/{session → legacy/session}/Voucher.ts +12 -8
  377. package/src/tempo/{session → legacy/session}/escrow.abi.ts +1 -0
  378. package/src/tempo/legacy/session/index.ts +8 -0
  379. package/src/tempo/server/AtomicStore.test-d.ts +16 -11
  380. package/src/tempo/server/Charge.test.ts +92 -14
  381. package/src/tempo/server/Charge.ts +18 -16
  382. package/src/tempo/server/Methods.ts +54 -8
  383. package/src/tempo/server/Sse.test.ts +2 -2
  384. package/src/tempo/server/index.ts +6 -5
  385. package/src/tempo/server/internal/html/package.json +1 -1
  386. package/src/tempo/server/internal/html.gen.ts +1 -1
  387. package/src/tempo/server/internal/request-body.test.ts +37 -4
  388. package/src/tempo/server/internal/request-body.ts +25 -6
  389. package/src/tempo/server/internal/transport.test.ts +4 -4
  390. package/src/tempo/server/internal/transport.ts +19 -10
  391. package/src/tempo/session/Snapshot.test.ts +41 -0
  392. package/src/tempo/session/Snapshot.ts +74 -0
  393. package/src/tempo/session/client/ChannelOps.test.ts +163 -0
  394. package/src/tempo/session/client/ChannelOps.ts +344 -0
  395. package/src/tempo/session/client/CredentialState.test.ts +645 -0
  396. package/src/tempo/session/client/CredentialState.ts +814 -0
  397. package/src/tempo/session/client/ReceiptCoordinator.ts +95 -0
  398. package/src/tempo/session/client/Runtime.test.ts +1092 -0
  399. package/src/tempo/session/client/Runtime.ts +986 -0
  400. package/src/tempo/session/client/Session.test.ts +734 -0
  401. package/src/tempo/session/client/Session.ts +97 -0
  402. package/src/tempo/session/client/SessionManager.test.ts +1308 -0
  403. package/src/tempo/session/client/SessionManager.ts +845 -0
  404. package/src/tempo/session/client/Transports.test.ts +837 -0
  405. package/src/tempo/session/client/Transports.ts +1292 -0
  406. package/src/tempo/session/client/index.ts +37 -0
  407. package/src/tempo/session/index.ts +7 -8
  408. package/src/tempo/session/precompile/Chain.integration.test.ts +321 -0
  409. package/src/tempo/session/precompile/Chain.test.ts +1258 -0
  410. package/src/tempo/session/precompile/Chain.ts +979 -0
  411. package/src/tempo/session/precompile/Channel.test.ts +138 -0
  412. package/src/tempo/session/precompile/Channel.ts +103 -0
  413. package/src/tempo/session/precompile/Protocol.test.ts +358 -0
  414. package/src/tempo/session/precompile/Protocol.ts +520 -0
  415. package/src/tempo/session/precompile/Voucher.test.ts +316 -0
  416. package/src/tempo/session/precompile/Voucher.ts +160 -0
  417. package/src/tempo/session/precompile/escrow.abi.ts +226 -0
  418. package/src/tempo/session/precompile/index.ts +33 -0
  419. package/src/tempo/session/server/ChannelOps.test.ts +129 -0
  420. package/src/tempo/session/server/ChannelOps.ts +157 -0
  421. package/src/tempo/session/{ChannelStore.test.ts → server/ChannelStore.test.ts} +536 -29
  422. package/src/tempo/session/server/ChannelStore.ts +835 -0
  423. package/src/tempo/session/server/CredentialVerification.test.ts +146 -0
  424. package/src/tempo/session/server/CredentialVerification.ts +710 -0
  425. package/src/tempo/session/server/MeteredStream.ts +88 -0
  426. package/src/tempo/session/server/RequestState.test.ts +531 -0
  427. package/src/tempo/session/server/RequestState.ts +499 -0
  428. package/src/tempo/session/server/Session.integration.test.ts +444 -0
  429. package/src/tempo/session/server/Session.test.ts +3253 -0
  430. package/src/tempo/session/server/Session.ts +543 -0
  431. package/src/tempo/session/server/Settlement.test.ts +242 -0
  432. package/src/tempo/session/server/Settlement.ts +470 -0
  433. package/src/tempo/session/{Sse.test.ts → server/Sse.test.ts} +37 -3
  434. package/src/tempo/session/server/Sse.ts +256 -0
  435. package/src/tempo/session/server/Transports.test.ts +346 -0
  436. package/src/tempo/session/server/Transports.ts +255 -0
  437. package/src/tempo/session/{Ws.test.ts → server/Ws.test.ts} +4 -4
  438. package/src/tempo/session/server/Ws.ts +384 -0
  439. package/src/tempo/session/server/index.ts +8 -0
  440. package/dist/tempo/client/ChannelOps.d.ts.map +0 -1
  441. package/dist/tempo/client/ChannelOps.js.map +0 -1
  442. package/dist/tempo/client/Session.d.ts.map +0 -1
  443. package/dist/tempo/client/Session.js.map +0 -1
  444. package/dist/tempo/client/SessionManager.d.ts.map +0 -1
  445. package/dist/tempo/client/SessionManager.js.map +0 -1
  446. package/dist/tempo/server/Session.d.ts.map +0 -1
  447. package/dist/tempo/server/Session.js.map +0 -1
  448. package/dist/tempo/session/Chain.d.ts.map +0 -1
  449. package/dist/tempo/session/Chain.js.map +0 -1
  450. package/dist/tempo/session/Channel.d.ts.map +0 -1
  451. package/dist/tempo/session/Channel.js.map +0 -1
  452. package/dist/tempo/session/ChannelStore.d.ts +0 -117
  453. package/dist/tempo/session/ChannelStore.d.ts.map +0 -1
  454. package/dist/tempo/session/ChannelStore.js +0 -172
  455. package/dist/tempo/session/ChannelStore.js.map +0 -1
  456. package/dist/tempo/session/Receipt.d.ts +0 -22
  457. package/dist/tempo/session/Receipt.d.ts.map +0 -1
  458. package/dist/tempo/session/Receipt.js +0 -34
  459. package/dist/tempo/session/Receipt.js.map +0 -1
  460. package/dist/tempo/session/Sse.d.ts.map +0 -1
  461. package/dist/tempo/session/Sse.js +0 -363
  462. package/dist/tempo/session/Sse.js.map +0 -1
  463. package/dist/tempo/session/Types.d.ts +0 -78
  464. package/dist/tempo/session/Types.d.ts.map +0 -1
  465. package/dist/tempo/session/Types.js.map +0 -1
  466. package/dist/tempo/session/Voucher.d.ts.map +0 -1
  467. package/dist/tempo/session/Voucher.js.map +0 -1
  468. package/dist/tempo/session/Ws.d.ts +0 -87
  469. package/dist/tempo/session/Ws.d.ts.map +0 -1
  470. package/dist/tempo/session/Ws.js +0 -443
  471. package/dist/tempo/session/Ws.js.map +0 -1
  472. package/dist/tempo/session/escrow.abi.js.map +0 -1
  473. package/src/tempo/session/ChannelStore.ts +0 -308
  474. package/src/tempo/session/Receipt.test.ts +0 -89
  475. package/src/tempo/session/Receipt.ts +0 -46
  476. package/src/tempo/session/Sse.ts +0 -462
  477. package/src/tempo/session/Types.ts +0 -86
  478. package/src/tempo/session/Ws.ts +0 -576
  479. /package/dist/tempo/{session → legacy/session}/Channel.js +0 -0
  480. /package/dist/tempo/{session → legacy/session}/Types.js +0 -0
  481. /package/src/tempo/{session → legacy/session}/Channel.test.ts +0 -0
  482. /package/src/tempo/{session → legacy/session}/Voucher.test.ts +0 -0
  483. /package/src/tempo/session/{Sse.fuzz.test.ts → server/Sse.fuzz.test.ts} +0 -0
@@ -2,6 +2,7 @@ import type { IncomingMessage, ServerResponse } from 'node:http'
2
2
  import { isDeepStrictEqual } from 'node:util'
3
3
 
4
4
  import * as Challenge from '../Challenge.js'
5
+ import * as Constants from '../Constants.js'
5
6
  import * as Credential from '../Credential.js'
6
7
  import * as Errors from '../Errors.js'
7
8
  import * as Expires from '../Expires.js'
@@ -326,12 +327,30 @@ type UniqueIntentHandlers<
326
327
  }
327
328
 
328
329
  /** Nested handlers: `mppx.tempo.charge(...)`, grouped by method name then intent. */
330
+ type PublicAlias<method extends Method.AnyServer> = method extends {
331
+ alias?: infer alias
332
+ }
333
+ ? Exclude<alias, undefined> extends infer definedAlias
334
+ ? definedAlias extends string
335
+ ? string extends definedAlias
336
+ ? never
337
+ : definedAlias
338
+ : never
339
+ : never
340
+ : never
341
+
342
+ type PublicIntent<method extends Method.AnyServer> = [PublicAlias<method>] extends [never]
343
+ ? method extends { intent: infer intent extends string }
344
+ ? intent
345
+ : never
346
+ : PublicAlias<method>
347
+
329
348
  type NestedHandlers<
330
349
  methods extends readonly Method.AnyServer[],
331
350
  transport extends Transport.AnyTransport,
332
351
  > = {
333
352
  [name in methods[number]['name'] as name extends ReservedKey ? never : name]: {
334
- [mi in Extract<methods[number], { name: name }> as mi['intent']]: MethodFn<
353
+ [mi in Extract<methods[number], { name: name }> as PublicIntent<mi>]: MethodFn<
335
354
  mi,
336
355
  EffectiveTransportOf<mi, transport>,
337
356
  NonNullable<mi['defaults']>
@@ -351,7 +370,12 @@ type Handlers<
351
370
  > &
352
371
  MethodExtensions<mi>
353
372
  } & UniqueIntentHandlers<methods, transport> &
354
- NestedHandlers<methods, transport>
373
+ NestedHandlers<methods, transport> & {
374
+ [mi in methods[number] as PublicAlias<mi> extends string
375
+ ? `${mi['name']}/${PublicAlias<mi>}`
376
+ : never]: MethodFn<mi, EffectiveTransportOf<mi, transport>, NonNullable<mi['defaults']>> &
377
+ MethodExtensions<mi>
378
+ }
355
379
 
356
380
  type MethodExtensions<method extends Method.AnyServer> = method extends {
357
381
  extensions?: (infer extensions) | undefined
@@ -364,7 +388,7 @@ type MethodExtensions<method extends Method.AnyServer> = method extends {
364
388
  /** Nested challenge generators: `mppx.challenge.tempo.charge(...)`. */
365
389
  type ChallengeHandlers<methods extends readonly Method.AnyServer[]> = {
366
390
  [name in methods[number]['name']]: {
367
- [mi in Extract<methods[number], { name: name }> as mi['intent']]: ChallengeFn<
391
+ [mi in Extract<methods[number], { name: name }> as PublicIntent<mi>]: ChallengeFn<
368
392
  mi,
369
393
  NonNullable<mi['defaults']>
370
394
  >
@@ -426,6 +450,7 @@ export function create<
426
450
  method: mi,
427
451
  realm,
428
452
  events: serverEvents as never,
453
+ preflight: mi.preflight as never,
429
454
  request: mi.request as never,
430
455
  respond: mi.respond as never,
431
456
  secretKey,
@@ -433,8 +458,11 @@ export function create<
433
458
  transport: (mi.transport ?? transport) as never,
434
459
  verify: mi.verify as never,
435
460
  })
461
+ const wireKey = `${mi.name}/${mi.intent}`
462
+ const aliasKey = mi.alias ? `${mi.name}/${mi.alias}` : undefined
436
463
  if (mi.extensions) Object.assign(fn, mi.extensions)
437
- handlers[`${mi.name}/${mi.intent}`] = fn
464
+ if (!aliasKey || !handlers[wireKey]) handlers[wireKey] = fn
465
+ if (aliasKey) handlers[aliasKey] = fn
438
466
  }
439
467
 
440
468
  // Also set shorthand intent key when there's no collision
@@ -445,16 +473,17 @@ export function create<
445
473
  // Build nested handlers: mppx.tempo.charge(...)
446
474
  for (const mi of methods) {
447
475
  if (!handlers[mi.name]) handlers[mi.name] = {}
448
- const fn = handlers[`${mi.name}/${mi.intent}`] as AnyMethodFn & { _method?: Method.AnyServer }
476
+ const key = mi.alias ? `${mi.name}/${mi.alias}` : `${mi.name}/${mi.intent}`
477
+ const fn = handlers[key] as AnyMethodFn & { _method?: Method.AnyServer }
449
478
  fn._method = mi
450
- ;(handlers[mi.name] as Record<string, unknown>)[mi.intent] = fn
479
+ ;(handlers[mi.name] as Record<string, unknown>)[mi.alias ?? mi.intent] = fn
451
480
  }
452
481
 
453
482
  // Build challenge generators: mppx.challenge.tempo.charge(...)
454
483
  const challengeHandlers: Record<string, Record<string, unknown>> = {}
455
484
  for (const mi of methods) {
456
485
  if (!challengeHandlers[mi.name]) challengeHandlers[mi.name] = {}
457
- challengeHandlers[mi.name]![mi.intent] = createChallengeFn({
486
+ challengeHandlers[mi.name]![mi.alias ?? mi.intent] = createChallengeFn({
458
487
  defaults: mi.defaults,
459
488
  method: mi,
460
489
  realm,
@@ -472,11 +501,13 @@ export function create<
472
501
  typeof input === 'string' ? Credential.deserialize(input) : input,
473
502
  )
474
503
 
475
- // Find matching method by name + intent
504
+ // Find matching method by name + intent, then use method-details markers
505
+ // when multiple handlers intentionally share the same wire identity.
476
506
  const { method: credMethod, intent: credIntent } = credential.challenge
477
- const mi = (methods as readonly Method.AnyServer[]).find(
507
+ const methodCandidates = (methods as readonly Method.AnyServer[]).filter(
478
508
  (m) => m.name === credMethod && m.intent === credIntent,
479
509
  )
510
+ const mi = selectVerificationMethod(methodCandidates, credential.challenge)
480
511
  const eventMethod =
481
512
  mi ?? ({ intent: credIntent, name: credMethod } satisfies ServerMethodDescriptor)
482
513
 
@@ -688,8 +719,8 @@ export function create<
688
719
  typeof methodOrKey === 'string'
689
720
  ? methodOrKey
690
721
  : typeof methodOrKey === 'function' && '_method' in methodOrKey
691
- ? `${(methodOrKey._method as Method.AnyServer).name}/${(methodOrKey._method as Method.AnyServer).intent}`
692
- : `${(methodOrKey as Method.AnyServer).name}/${(methodOrKey as Method.AnyServer).intent}`
722
+ ? `${(methodOrKey._method as Method.AnyServer).name}/${(methodOrKey._method as Method.AnyServer).alias ?? (methodOrKey._method as Method.AnyServer).intent}`
723
+ : `${(methodOrKey as Method.AnyServer).name}/${(methodOrKey as Method.AnyServer).alias ?? (methodOrKey as Method.AnyServer).intent}`
693
724
  const handlerFn = handlers[key] as AnyMethodFn | undefined
694
725
  if (!handlerFn)
695
726
  throw new Error(`No handler for "${key}". Is this method in your methods array?`)
@@ -761,6 +792,7 @@ function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.R
761
792
  defaults,
762
793
  events,
763
794
  method,
795
+ preflight,
764
796
  realm,
765
797
  respond,
766
798
  secretKey,
@@ -772,75 +804,83 @@ function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.R
772
804
  return (options) => {
773
805
  const { description, meta, scope, ...rest } = options
774
806
  const staticMeta = Scope.merge({ meta, scope })
807
+ const internal: ConfiguredHandler['_internal'] = {
808
+ ...method,
809
+ ...defaults,
810
+ ...options,
811
+ ...(staticMeta !== undefined ? { meta: staticMeta } : {}),
812
+ name: method.name,
813
+ intent: method.intent,
814
+ html: method.html,
815
+ _canonicalRequest: PaymentRequest.fromMethod(method, { ...defaults, ...rest }),
816
+ _stableBinding: stableBinding as never,
817
+ }
775
818
 
776
- return Object.assign(
777
- async (input: Transport.InputOf): Promise<MethodFn.Response> => {
778
- if (method.html && isServiceWorkerRequest(input))
779
- return {
780
- status: 402,
781
- challenge: createServiceWorkerResponse(),
782
- } as MethodFn.Response<Transport.Http>
783
-
784
- const expires =
785
- 'expires' in options
786
- ? normalizeExpires(options.expires as z.DatetimeInput | undefined)
787
- : Expires.minutes(5)
788
- const capturedRequest = await captureRequest(transport, input)
789
- const effectiveMeta =
790
- scope === undefined && input instanceof globalThis.Request
791
- ? Scope.merge({ meta: staticMeta, scope: Scope.get(input) })
792
- : staticMeta
793
-
794
- // Extract credential once — getCredential may have side effects (e.g. SSE transports).
795
- let [credential, credentialError] = (() => {
796
- try {
797
- const credential = transport.getCredential(input) as Credential.Credential | null
798
- return [credential ? hydrateCredentialMeta(credential) : null, undefined] as const
799
- } catch (e) {
800
- return [null, e as Error] as const
801
- }
802
- })()
819
+ const handler = async (input: Transport.InputOf): Promise<MethodFn.Response> => {
820
+ if (method.html && isServiceWorkerRequest(input))
821
+ return {
822
+ status: 402,
823
+ challenge: createServiceWorkerResponse(),
824
+ } as MethodFn.Response<Transport.Http>
803
825
 
804
- const emitChallenge = async (parameters: {
805
- challenge: Challenge.Challenge
806
- credential?: Credential.Credential | null | undefined
807
- error?: Errors.PaymentError | undefined
808
- html?: Method.Method['html'] | undefined
809
- request: Record<string, unknown>
810
- }) => {
811
- const response = await transport.respondChallenge({
812
- challenge: parameters.challenge,
813
- input,
814
- ...(parameters.error && { error: parameters.error }),
815
- ...(parameters.html && { html: parameters.html }),
816
- })
817
- if (isIssuedChallengeResponse(response))
818
- await events.emit(
819
- 'challenge.created',
820
- createChallengeContext({
821
- capturedRequest,
822
- challenge: parameters.challenge,
823
- credential: parameters.credential,
824
- error: parameters.error,
825
- input,
826
- method,
827
- request: parameters.request,
828
- }) as never,
829
- )
830
- return response
826
+ const expires =
827
+ 'expires' in options
828
+ ? normalizeExpires(options.expires as z.DatetimeInput | undefined)
829
+ : Expires.minutes(5)
830
+ const capturedRequest = await captureRequest(transport, input)
831
+ const effectiveMeta =
832
+ scope === undefined && input instanceof globalThis.Request
833
+ ? Scope.merge({ meta: staticMeta, scope: Scope.get(input) })
834
+ : staticMeta
835
+
836
+ // Extract credential once — getCredential may have side effects (e.g. SSE transports).
837
+ let [credential, credentialError] = (() => {
838
+ try {
839
+ const credential = transport.getCredential(input) as Credential.Credential | null
840
+ return [credential ? hydrateCredentialMeta(credential) : null, undefined] as const
841
+ } catch (e) {
842
+ return [null, e as Error] as const
843
+ }
844
+ })()
845
+
846
+ if (preflight && input instanceof globalThis.Request) {
847
+ const response = await preflight({
848
+ capturedRequest,
849
+ credential,
850
+ expires,
851
+ input,
852
+ options: { ...defaults, ...rest },
853
+ realm: realm ?? new URL(input.url).hostname ?? 'MPP Payment',
854
+ secretKey,
855
+ } as never)
856
+ if (response) {
857
+ if (response.status === 402) return { challenge: response, status: 402 }
858
+ return {
859
+ status: 200,
860
+ withReceipt() {
861
+ return response
862
+ },
863
+ } as MethodFn.Response
831
864
  }
865
+ }
832
866
 
833
- const emitPaymentFailed = async (parameters: {
834
- challenge: Challenge.Challenge
835
- credential: Credential.Credential | null
836
- error: Errors.PaymentError
837
- request: Record<string, unknown>
838
- retryChallenge?: Challenge.Challenge | undefined
839
- submittedChallenge?: Challenge.Challenge | undefined
840
- }) => {
867
+ const emitChallenge = async (parameters: {
868
+ challenge: Challenge.Challenge
869
+ credential?: Credential.Credential | null | undefined
870
+ error?: Errors.PaymentError | undefined
871
+ html?: Method.Method['html'] | undefined
872
+ request: Record<string, unknown>
873
+ }) => {
874
+ const response = await transport.respondChallenge({
875
+ challenge: parameters.challenge,
876
+ input,
877
+ ...(parameters.error && { error: parameters.error }),
878
+ ...(parameters.html && { html: parameters.html }),
879
+ })
880
+ if (isIssuedChallengeResponse(response))
841
881
  await events.emit(
842
- 'payment.failed',
843
- createPaymentFailedContext({
882
+ 'challenge.created',
883
+ createChallengeContext({
844
884
  capturedRequest,
845
885
  challenge: parameters.challenge,
846
886
  credential: parameters.credential,
@@ -848,272 +888,199 @@ function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.R
848
888
  input,
849
889
  method,
850
890
  request: parameters.request,
851
- retryChallenge: parameters.retryChallenge,
852
- submittedChallenge: parameters.submittedChallenge,
853
891
  }) as never,
854
892
  )
855
- }
893
+ return response
894
+ }
895
+
896
+ const emitPaymentFailed = async (parameters: {
897
+ challenge: Challenge.Challenge
898
+ credential: Credential.Credential | null
899
+ error: Errors.PaymentError
900
+ request: Record<string, unknown>
901
+ retryChallenge?: Challenge.Challenge | undefined
902
+ submittedChallenge?: Challenge.Challenge | undefined
903
+ }) => {
904
+ await events.emit(
905
+ 'payment.failed',
906
+ createPaymentFailedContext({
907
+ capturedRequest,
908
+ challenge: parameters.challenge,
909
+ credential: parameters.credential,
910
+ error: parameters.error,
911
+ input,
912
+ method,
913
+ request: parameters.request,
914
+ retryChallenge: parameters.retryChallenge,
915
+ submittedChallenge: parameters.submittedChallenge,
916
+ }) as never,
917
+ )
918
+ }
856
919
 
857
- const routeChallenge = await resolveRouteChallenge({
920
+ const routeChallenge = await resolveRouteChallenge({
921
+ capturedRequest,
922
+ credential,
923
+ defaults,
924
+ description,
925
+ expires,
926
+ meta: effectiveMeta,
927
+ method,
928
+ realm,
929
+ request: parameters.request,
930
+ routeRequest: rest,
931
+ secretKey,
932
+ }).catch(async (e) => {
933
+ if (!(e instanceof Errors.PaymentError)) throw e
934
+ const challenge = createFallbackChallenge({
858
935
  capturedRequest,
859
- credential,
860
- defaults,
936
+ defaults: defaults ?? {},
861
937
  description,
862
938
  expires,
863
939
  meta: effectiveMeta,
864
940
  method,
865
941
  realm,
866
- request: parameters.request,
867
942
  routeRequest: rest,
868
943
  secretKey,
869
- }).catch(async (e) => {
870
- if (!(e instanceof Errors.PaymentError)) throw e
871
- const challenge = createFallbackChallenge({
872
- capturedRequest,
873
- defaults: defaults ?? {},
874
- description,
875
- expires,
876
- meta: effectiveMeta,
877
- method,
878
- realm,
879
- routeRequest: rest,
880
- secretKey,
881
- })
882
- if (credential)
883
- await emitPaymentFailed({
884
- challenge,
885
- credential,
886
- error: e,
887
- request: challenge.request,
888
- retryChallenge: challenge,
889
- submittedChallenge: credential.challenge,
890
- })
891
- const response = await emitChallenge({
944
+ })
945
+ if (credential)
946
+ await emitPaymentFailed({
892
947
  challenge,
893
948
  credential,
894
- request: challenge.request,
895
949
  error: e,
896
- html: method.html,
950
+ request: challenge.request,
951
+ retryChallenge: challenge,
952
+ submittedChallenge: credential.challenge,
897
953
  })
898
- return { response }
954
+ const response = await emitChallenge({
955
+ challenge,
956
+ credential,
957
+ request: challenge.request,
958
+ error: e,
959
+ html: method.html,
899
960
  })
900
- if ('response' in routeChallenge) return { challenge: routeChallenge.response, status: 402 }
901
- const { challenge, parsedRequest, request } = routeChallenge
961
+ return { response }
962
+ })
963
+ if ('response' in routeChallenge) return { challenge: routeChallenge.response, status: 402 }
964
+ const { challenge, parsedRequest, request } = routeChallenge
965
+ internal._canonicalRequest = parsedRequest
902
966
 
903
- if (credential && transport.bindCredential) {
904
- try {
905
- credential = hydrateCredentialMeta(
906
- (await transport.bindCredential({
907
- challenge,
908
- credential,
909
- input,
910
- })) as Credential.Credential,
911
- )
912
- } catch (e) {
913
- credential = null
914
- credentialError = e as Error
915
- }
967
+ if (credential && transport.bindCredential) {
968
+ try {
969
+ credential = hydrateCredentialMeta(
970
+ (await transport.bindCredential({
971
+ challenge,
972
+ credential,
973
+ input,
974
+ })) as Credential.Credential,
975
+ )
976
+ } catch (e) {
977
+ credential = null
978
+ credentialError = e as Error
916
979
  }
980
+ }
917
981
 
918
- // Credential was provided but malformed
919
- if (credentialError) {
920
- const reason = getSafeCredentialReason(credentialError)
921
- const error = new Errors.MalformedCredentialError(reason ? { reason } : {})
922
- await emitPaymentFailed({
923
- challenge,
924
- credential: null,
925
- error,
926
- request: parsedRequest,
927
- retryChallenge: challenge,
928
- })
929
- const response = await emitChallenge({
930
- challenge,
931
- credential: null,
932
- request: parsedRequest,
933
- error,
934
- html: method.html,
935
- })
936
- return { challenge: response, status: 402 }
937
- }
982
+ // Credential was provided but malformed
983
+ if (credentialError) {
984
+ const reason = getSafeCredentialReason(credentialError)
985
+ const error = new Errors.MalformedCredentialError(reason ? { reason } : {})
986
+ await emitPaymentFailed({
987
+ challenge,
988
+ credential: null,
989
+ error,
990
+ request: parsedRequest,
991
+ retryChallenge: challenge,
992
+ })
993
+ const response = await emitChallenge({
994
+ challenge,
995
+ credential: null,
996
+ request: parsedRequest,
997
+ error,
998
+ html: method.html,
999
+ })
1000
+ return { challenge: response, status: 402 }
1001
+ }
938
1002
 
939
- const success = (
940
- receiptData: Receipt.Receipt,
941
- options: {
942
- challengeId?: string | undefined
943
- credentialForReceipt?: Credential.Credential | undefined
944
- envelopeForReceipt?: Method.VerifiedChallengeEnvelope | undefined
945
- managementResponse?: globalThis.Response | undefined
946
- } = {},
947
- ): MethodFn.Response => {
948
- const {
949
- challengeId = challenge.id,
950
- credentialForReceipt = { challenge, payload: {} } as Credential.Credential,
951
- envelopeForReceipt,
952
- managementResponse,
953
- } = options
1003
+ const success = (
1004
+ receiptData: Receipt.Receipt,
1005
+ options: {
1006
+ challengeId?: string | undefined
1007
+ credentialForReceipt?: Credential.Credential | undefined
1008
+ envelopeForReceipt?: Method.VerifiedChallengeEnvelope | undefined
1009
+ managementResponse?: globalThis.Response | undefined
1010
+ } = {},
1011
+ ): MethodFn.Response => {
1012
+ const {
1013
+ challengeId = challenge.id,
1014
+ credentialForReceipt = { challenge, payload: {} } as Credential.Credential,
1015
+ envelopeForReceipt,
1016
+ managementResponse,
1017
+ } = options
954
1018
 
955
- return {
956
- status: 200,
957
- withReceipt<response>(response?: response) {
958
- if (managementResponse) {
959
- return transport.respondReceipt({
960
- challengeId,
961
- credential: credentialForReceipt,
962
- ...(envelopeForReceipt ? { envelope: envelopeForReceipt } : {}),
963
- input,
964
- receipt: receiptData,
965
- response: managementResponse as never,
966
- }) as response
967
- }
968
- if (!response) throw new MissingReceiptResponseError()
1019
+ return {
1020
+ status: 200,
1021
+ withReceipt<response>(response?: response) {
1022
+ if (managementResponse) {
969
1023
  return transport.respondReceipt({
970
1024
  challengeId,
971
1025
  credential: credentialForReceipt,
972
1026
  ...(envelopeForReceipt ? { envelope: envelopeForReceipt } : {}),
973
1027
  input,
974
1028
  receipt: receiptData,
975
- response: response as never,
1029
+ response: managementResponse as never,
976
1030
  }) as response
977
- },
978
- }
1031
+ }
1032
+ if (!response) throw new MissingReceiptResponseError()
1033
+ return transport.respondReceipt({
1034
+ challengeId,
1035
+ credential: credentialForReceipt,
1036
+ ...(envelopeForReceipt ? { envelope: envelopeForReceipt } : {}),
1037
+ input,
1038
+ receipt: receiptData,
1039
+ response: response as never,
1040
+ }) as response
1041
+ },
979
1042
  }
1043
+ }
980
1044
 
981
- // No credential provided—issue challenge
982
- if (!credential) {
983
- if (authorize && input instanceof globalThis.Request) {
984
- try {
985
- const authorized = await authorize({
986
- challenge,
987
- input,
988
- request: challenge.request,
989
- } as never)
990
- if (authorized) {
991
- await events.emit(
992
- 'payment.success',
993
- createPaymentSuccessContext({
994
- capturedRequest,
995
- challenge,
996
- input,
997
- method,
998
- receipt: authorized.receipt,
999
- request: parsedRequest,
1000
- }) as never,
1001
- )
1002
- return success(authorized.receipt, {
1003
- managementResponse: authorized.response,
1004
- })
1005
- }
1006
- } catch (e) {
1007
- if (!(e instanceof Errors.PaymentError))
1008
- console.error('mppx: internal authorization error', e)
1009
- const error =
1010
- e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError()
1011
- await emitPaymentFailed({
1012
- challenge,
1013
- credential: null,
1014
- error,
1015
- request: parsedRequest,
1016
- retryChallenge: challenge,
1017
- })
1018
- const response = await emitChallenge({
1019
- challenge,
1020
- request: parsedRequest,
1021
- error,
1022
- html: method.html,
1045
+ // No credential provided—issue challenge
1046
+ if (!credential) {
1047
+ if (authorize && input instanceof globalThis.Request) {
1048
+ try {
1049
+ const authorized = await authorize({
1050
+ challenge,
1051
+ input,
1052
+ request: challenge.request,
1053
+ } as never)
1054
+ if (authorized) {
1055
+ await events.emit(
1056
+ 'payment.success',
1057
+ createPaymentSuccessContext({
1058
+ capturedRequest,
1059
+ challenge,
1060
+ input,
1061
+ method,
1062
+ receipt: authorized.receipt,
1063
+ request: parsedRequest,
1064
+ }) as never,
1065
+ )
1066
+ return success(authorized.receipt, {
1067
+ managementResponse: authorized.response,
1023
1068
  })
1024
- return { challenge: response, status: 402 }
1025
1069
  }
1026
- }
1027
-
1028
- const error = new Errors.PaymentRequiredError({ description })
1029
- const response = await emitChallenge({
1030
- challenge,
1031
- credential: null,
1032
- request: parsedRequest,
1033
- error,
1034
- html: method.html,
1035
- })
1036
- return { challenge: response, status: 402 }
1037
- }
1038
-
1039
- // ── Tier 1: HMAC provenance check (primary gate) ──────────────────
1040
- //
1041
- // Recompute the HMAC-SHA256 over the credential's echoed challenge
1042
- // parameters (realm|method|intent|request|expires|digest|opaque) and
1043
- // compare to the echoed `id`. This proves the challenge was issued by
1044
- // this server with these exact parameters — including opaque/meta,
1045
- // expires, and the full serialized request blob.
1046
- //
1047
- // This is the authoritative binding per §5.1.2.1.1 of the spec
1048
- // (https://paymentauth.org/draft-httpauth-payment-00.html#section-5.1.2.1.1).
1049
- // No database lookup is needed; the HMAC is stateless verification.
1050
- if (!Challenge.verify(credential.challenge, { secretKey })) {
1051
- const error = new Errors.InvalidChallengeError({
1052
- id: credential.challenge.id,
1053
- reason: 'challenge was not issued by this server',
1054
- })
1055
- await emitPaymentFailed({
1056
- challenge,
1057
- credential,
1058
- error,
1059
- request: parsedRequest,
1060
- retryChallenge: challenge,
1061
- submittedChallenge: credential.challenge,
1062
- })
1063
- const response = await emitChallenge({
1064
- challenge,
1065
- credential,
1066
- request: parsedRequest,
1067
- error,
1068
- html: method.html,
1069
- })
1070
- return { challenge: response, status: 402 }
1071
- }
1072
-
1073
- // ── Tier 2: Pinned field safety net ──────────────────────────────
1074
- //
1075
- // The HMAC check above (Tier 1) is the primary gate — it already
1076
- // covers ALL challenge fields including opaque, digest, and the full
1077
- // serialized request. So why this second check?
1078
- //
1079
- // The `request()` hook can produce credential-dependent output: for
1080
- // example, `feePayer` may differ between the 402 challenge call (no
1081
- // credential) and the credential-bearing call. This means the
1082
- // recomputed challenge here has a different `request` blob — and
1083
- // thus a different HMAC — than the original challenge the client
1084
- // echoes back. The HMAC check above verifies the *echoed* challenge
1085
- // was signed by us, but it cannot verify that the echoed challenge
1086
- // matches *this route's current configuration* when the request
1087
- // hook transforms fields between calls.
1088
- //
1089
- // This check compares the fields that MUST be stable across both
1090
- // calls. That includes the economically significant request fields
1091
- // plus `opaque`, which can carry route-scoping metadata (for example,
1092
- // sibling route identity) that must not be replayable across handlers.
1093
- // `expires` still is not pinned here because its default is generated
1094
- // per invocation, and `digest` is already bound by the echoed HMAC.
1095
- {
1096
- const mismatch = getChallengeBindingMismatch(
1097
- challenge,
1098
- credential.challenge,
1099
- stableBinding as never,
1100
- )
1101
- if (mismatch) {
1102
- const error = new Errors.InvalidChallengeError({
1103
- id: credential.challenge.id,
1104
- reason: `credential ${mismatch} does not match this route's requirements`,
1105
- })
1070
+ } catch (e) {
1071
+ if (!(e instanceof Errors.PaymentError))
1072
+ console.error('mppx: internal authorization error', e)
1073
+ const error =
1074
+ e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError()
1106
1075
  await emitPaymentFailed({
1107
1076
  challenge,
1108
- credential,
1077
+ credential: null,
1109
1078
  error,
1110
1079
  request: parsedRequest,
1111
1080
  retryChallenge: challenge,
1112
- submittedChallenge: credential.challenge,
1113
1081
  })
1114
1082
  const response = await emitChallenge({
1115
1083
  challenge,
1116
- credential,
1117
1084
  request: parsedRequest,
1118
1085
  error,
1119
1086
  html: method.html,
@@ -1122,35 +1089,84 @@ function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.R
1122
1089
  }
1123
1090
  }
1124
1091
 
1125
- // Reject credentials without expires (fail-closed) or with expired timestamp
1126
- try {
1127
- Expires.assert(credential.challenge.expires, credential.challenge.id)
1128
- } catch (error) {
1129
- await emitPaymentFailed({
1130
- challenge,
1131
- credential,
1132
- error: error as Errors.PaymentError,
1133
- request: parsedRequest,
1134
- retryChallenge: challenge,
1135
- submittedChallenge: credential.challenge,
1136
- })
1137
- const response = await emitChallenge({
1138
- challenge,
1139
- credential,
1140
- request: parsedRequest,
1141
- error: error as Errors.PaymentError,
1092
+ const error = new Errors.PaymentRequiredError({ description })
1093
+ const response = await emitChallenge({
1094
+ challenge,
1095
+ credential: null,
1096
+ request: parsedRequest,
1097
+ error,
1098
+ html: method.html,
1099
+ })
1100
+ return { challenge: response, status: 402 }
1101
+ }
1102
+
1103
+ // ── Tier 1: HMAC provenance check (primary gate) ──────────────────
1104
+ //
1105
+ // Recompute the HMAC-SHA256 over the credential's echoed challenge
1106
+ // parameters (realm|method|intent|request|expires|digest|opaque) and
1107
+ // compare to the echoed `id`. This proves the challenge was issued by
1108
+ // this server with these exact parameters — including opaque/meta,
1109
+ // expires, and the full serialized request blob.
1110
+ //
1111
+ // This is the authoritative binding per §5.1.2.1.1 of the spec
1112
+ // (https://paymentauth.org/draft-httpauth-payment-00.html#section-5.1.2.1.1).
1113
+ // No database lookup is needed; the HMAC is stateless verification.
1114
+ if (!Challenge.verify(credential.challenge, { secretKey })) {
1115
+ const error = new Errors.InvalidChallengeError({
1116
+ id: credential.challenge.id,
1117
+ reason: 'challenge was not issued by this server',
1118
+ })
1119
+ await emitPaymentFailed({
1120
+ challenge,
1121
+ credential,
1122
+ error,
1123
+ request: parsedRequest,
1124
+ retryChallenge: challenge,
1125
+ submittedChallenge: credential.challenge,
1126
+ })
1127
+ const response = await emitChallenge({
1128
+ challenge,
1129
+ credential,
1130
+ request: parsedRequest,
1131
+ error,
1132
+ html: method.html,
1133
+ })
1134
+ return { challenge: response, status: 402 }
1135
+ }
1136
+
1137
+ // ── Tier 2: Pinned field safety net ──────────────────────────────
1138
+ //
1139
+ // The HMAC check above (Tier 1) is the primary gate — it already
1140
+ // covers ALL challenge fields including opaque, digest, and the full
1141
+ // serialized request. So why this second check?
1142
+ //
1143
+ // The `request()` hook can produce credential-dependent output: for
1144
+ // example, `feePayer` may differ between the 402 challenge call (no
1145
+ // credential) and the credential-bearing call. This means the
1146
+ // recomputed challenge here has a different `request` blob — and
1147
+ // thus a different HMAC — than the original challenge the client
1148
+ // echoes back. The HMAC check above verifies the *echoed* challenge
1149
+ // was signed by us, but it cannot verify that the echoed challenge
1150
+ // matches *this route's current configuration* when the request
1151
+ // hook transforms fields between calls.
1152
+ //
1153
+ // This check compares the fields that MUST be stable across both
1154
+ // calls. That includes the economically significant request fields
1155
+ // plus `opaque`, which can carry route-scoping metadata (for example,
1156
+ // sibling route identity) that must not be replayable across handlers.
1157
+ // `expires` still is not pinned here because its default is generated
1158
+ // per invocation, and `digest` is already bound by the echoed HMAC.
1159
+ {
1160
+ const mismatch = getChallengeBindingMismatch(
1161
+ challenge,
1162
+ credential.challenge,
1163
+ stableBinding as never,
1164
+ )
1165
+ if (mismatch) {
1166
+ const error = new Errors.InvalidChallengeError({
1167
+ id: credential.challenge.id,
1168
+ reason: `credential ${mismatch} does not match this route's requirements`,
1142
1169
  })
1143
- return { challenge: response, status: 402 }
1144
- }
1145
- // Validate payload structure against method schema
1146
- let parsedCredential: Credential.Credential
1147
- try {
1148
- parsedCredential = withParsedCredentialPayload(
1149
- credential,
1150
- method.schema.credential.payload.parse(credential.payload),
1151
- )
1152
- } catch {
1153
- const error = new Errors.InvalidPayloadError()
1154
1170
  await emitPaymentFailed({
1155
1171
  challenge,
1156
1172
  credential,
@@ -1164,92 +1180,129 @@ function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.R
1164
1180
  credential,
1165
1181
  request: parsedRequest,
1166
1182
  error,
1183
+ html: method.html,
1167
1184
  })
1168
1185
  return { challenge: response, status: 402 }
1169
1186
  }
1187
+ }
1170
1188
 
1171
- const envelope: Method.VerifiedChallengeEnvelope = Object.freeze({
1172
- capturedRequest,
1173
- challenge: credential.challenge,
1174
- credential: parsedCredential,
1189
+ // Reject credentials without expires (fail-closed) or with expired timestamp
1190
+ try {
1191
+ Expires.assert(credential.challenge.expires, credential.challenge.id)
1192
+ } catch (error) {
1193
+ await emitPaymentFailed({
1194
+ challenge,
1195
+ credential,
1196
+ error: error as Errors.PaymentError,
1197
+ request: parsedRequest,
1198
+ retryChallenge: challenge,
1199
+ submittedChallenge: credential.challenge,
1200
+ })
1201
+ const response = await emitChallenge({
1202
+ challenge,
1203
+ credential,
1204
+ request: parsedRequest,
1205
+ error: error as Errors.PaymentError,
1206
+ })
1207
+ return { challenge: response, status: 402 }
1208
+ }
1209
+ // Validate payload structure against method schema
1210
+ let parsedCredential: Credential.Credential
1211
+ try {
1212
+ parsedCredential = withParsedCredentialPayload(
1213
+ credential,
1214
+ method.schema.credential.payload.parse(credential.payload),
1215
+ )
1216
+ } catch {
1217
+ const error = new Errors.InvalidPayloadError()
1218
+ await emitPaymentFailed({
1219
+ challenge,
1220
+ credential,
1221
+ error,
1175
1222
  request: parsedRequest,
1223
+ retryChallenge: challenge,
1224
+ submittedChallenge: credential.challenge,
1176
1225
  })
1226
+ const response = await emitChallenge({
1227
+ challenge,
1228
+ credential,
1229
+ request: parsedRequest,
1230
+ error,
1231
+ })
1232
+ return { challenge: response, status: 402 }
1233
+ }
1177
1234
 
1178
- // User-provided verification (e.g., check signature, submit tx, verify payment).
1179
- // If verification fails, re-issue the challenge so the client can retry.
1180
- let receiptData: Receipt.Receipt
1181
- try {
1182
- receiptData = await verify({ credential: parsedCredential, envelope, request } as never)
1183
- } catch (e) {
1184
- if (!(e instanceof Errors.PaymentError))
1185
- console.error('mppx: internal verification error', e)
1186
- const error = e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError()
1187
- await emitPaymentFailed({
1188
- challenge,
1189
- credential: parsedCredential,
1190
- error,
1191
- request: parsedRequest,
1192
- retryChallenge: challenge,
1193
- submittedChallenge: credential.challenge,
1194
- })
1195
- const response = await emitChallenge({
1196
- challenge,
1197
- credential: parsedCredential,
1198
- request: parsedRequest,
1199
- error,
1200
- })
1201
- return { challenge: response, status: 402 }
1202
- }
1235
+ const envelope: Method.VerifiedChallengeEnvelope = Object.freeze({
1236
+ capturedRequest,
1237
+ challenge: credential.challenge,
1238
+ credential: parsedCredential,
1239
+ request: parsedRequest,
1240
+ })
1203
1241
 
1204
- // If the method's `respond` hook returns a Response, it means this
1205
- // request is a management action (e.g. channel open, voucher POST)
1206
- // and the user's route handler should NOT run. `withReceipt()` will
1207
- // return the management response directly. If undefined, `withReceipt()`
1208
- // expects the caller to pass the user handler's response instead.
1209
- const managementResponse = respond
1210
- ? await respond({
1211
- credential: parsedCredential,
1212
- envelope,
1213
- input,
1214
- receipt: receiptData,
1215
- request,
1216
- } as never)
1217
- : undefined
1242
+ // User-provided verification (e.g., check signature, submit tx, verify payment).
1243
+ // If verification fails, re-issue the challenge so the client can retry.
1244
+ let receiptData: Receipt.Receipt
1245
+ try {
1246
+ receiptData = await verify({ credential: parsedCredential, envelope, request } as never)
1247
+ } catch (e) {
1248
+ if (!(e instanceof Errors.PaymentError))
1249
+ console.error('mppx: internal verification error', e)
1250
+ const error = e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError()
1251
+ await emitPaymentFailed({
1252
+ challenge,
1253
+ credential: parsedCredential,
1254
+ error,
1255
+ request: parsedRequest,
1256
+ retryChallenge: challenge,
1257
+ submittedChallenge: credential.challenge,
1258
+ })
1259
+ const response = await emitChallenge({
1260
+ challenge,
1261
+ credential: parsedCredential,
1262
+ request: parsedRequest,
1263
+ error,
1264
+ })
1265
+ return { challenge: response, status: 402 }
1266
+ }
1218
1267
 
1219
- await events.emit(
1220
- 'payment.success',
1221
- createPaymentSuccessContext({
1222
- capturedRequest,
1223
- challenge: credential.challenge,
1268
+ // If the method's `respond` hook returns a Response, it means this
1269
+ // request is a management action (e.g. channel open, voucher POST)
1270
+ // and the user's route handler should NOT run. `withReceipt()` will
1271
+ // return the management response directly. If undefined, `withReceipt()`
1272
+ // expects the caller to pass the user handler's response instead.
1273
+ const managementResponse = respond
1274
+ ? await respond({
1224
1275
  credential: parsedCredential,
1225
1276
  envelope,
1226
1277
  input,
1227
- method,
1228
1278
  receipt: receiptData,
1229
- request: parsedRequest,
1230
- }) as never,
1231
- )
1279
+ request,
1280
+ } as never)
1281
+ : undefined
1232
1282
 
1233
- return success(receiptData, {
1234
- challengeId: credential.challenge.id,
1235
- credentialForReceipt: parsedCredential,
1236
- envelopeForReceipt: envelope,
1237
- managementResponse,
1238
- })
1239
- },
1240
- {
1241
- _internal: {
1242
- ...method,
1243
- ...defaults,
1244
- ...options,
1245
- ...(staticMeta !== undefined ? { meta: staticMeta } : {}),
1246
- name: method.name,
1247
- intent: method.intent,
1248
- _canonicalRequest: PaymentRequest.fromMethod(method, { ...defaults, ...rest }),
1249
- _stableBinding: stableBinding as never,
1250
- },
1251
- },
1252
- )
1283
+ await events.emit(
1284
+ 'payment.success',
1285
+ createPaymentSuccessContext({
1286
+ capturedRequest,
1287
+ challenge: credential.challenge,
1288
+ credential: parsedCredential,
1289
+ envelope,
1290
+ input,
1291
+ method,
1292
+ receipt: receiptData,
1293
+ request: parsedRequest,
1294
+ }) as never,
1295
+ )
1296
+
1297
+ return success(receiptData, {
1298
+ challengeId: credential.challenge.id,
1299
+ credentialForReceipt: parsedCredential,
1300
+ envelopeForReceipt: envelope,
1301
+ managementResponse,
1302
+ })
1303
+ }
1304
+
1305
+ return Object.assign(handler, { _internal: internal })
1253
1306
  }
1254
1307
  }
1255
1308
 
@@ -1305,6 +1358,7 @@ declare namespace createMethodFn {
1305
1358
  defaults?: defaults
1306
1359
  method: method
1307
1360
  events: ServerEventDispatcher<readonly [method], transport>
1361
+ preflight?: Method.PreflightFn<method>
1308
1362
  realm: string | undefined
1309
1363
  request?: Method.RequestFn<method>
1310
1364
  respond?: Method.RespondFn<method>
@@ -1767,7 +1821,7 @@ function captureRequestFromInput(input: unknown): Method.CapturedRequest {
1767
1821
  }
1768
1822
 
1769
1823
  const coreBindingFields = ['amount', 'currency', 'recipient'] as const
1770
- const methodBindingFields = ['chainId', 'memo', 'splits', 'unitType'] as const
1824
+ const methodBindingFields = ['chainId', 'memo', 'sessionProtocol', 'splits', 'unitType'] as const
1771
1825
  const pinnedRequestBindingFields = [...coreBindingFields, ...methodBindingFields] as const
1772
1826
 
1773
1827
  type CoreBindingField = (typeof coreBindingFields)[number]
@@ -1776,6 +1830,31 @@ type PinnedRequestBindingField = (typeof pinnedRequestBindingFields)[number]
1776
1830
  type PinnedChallengeField = 'method' | 'intent' | 'realm' | 'opaque' | PinnedRequestBindingField
1777
1831
  type StableBinding = Record<string, unknown>
1778
1832
 
1833
+ function selectVerificationMethod(
1834
+ methods: readonly Method.AnyServer[],
1835
+ challenge: Challenge.Challenge,
1836
+ ): Method.AnyServer | undefined {
1837
+ if (methods.length <= 1) return methods[0]
1838
+ if (
1839
+ challenge.method !== Constants.Methods.tempo ||
1840
+ challenge.intent !== Constants.Intents.session
1841
+ )
1842
+ return methods[0]
1843
+
1844
+ const sessionProtocolMarker = Constants.getMethodDetail(
1845
+ challenge.request.methodDetails,
1846
+ Constants.MethodDetailKeys.sessionProtocol,
1847
+ )
1848
+ if (
1849
+ sessionProtocolMarker === undefined ||
1850
+ sessionProtocolMarker === Constants.SessionProtocols.v1
1851
+ )
1852
+ return methods.find((method) => method.alias === 'sessionLegacy') ?? methods[0]
1853
+ if (sessionProtocolMarker === Constants.SessionProtocols.v2)
1854
+ return methods.find((method) => method.alias === undefined) ?? methods[0]
1855
+ return undefined
1856
+ }
1857
+
1779
1858
  function getChallengeBindingMismatch(
1780
1859
  expectedChallenge: Challenge.Challenge,
1781
1860
  actualChallenge: Challenge.Challenge,
@@ -1856,6 +1935,7 @@ function getPinnedRequestBinding(request: Record<string, unknown>): PinnedReques
1856
1935
  const currency = normalizeScalar(request.currency ?? methodDetails.currency)
1857
1936
  const memo = normalizeHex(methodDetails.memo)
1858
1937
  const recipient = normalizeScalar(request.recipient ?? methodDetails.recipient)
1938
+ const sessionProtocol = normalizeScalar(methodDetails.sessionProtocol)
1859
1939
  const splits = normalizeComparable(methodDetails.splits)
1860
1940
  const unitType = normalizeScalar(request.unitType ?? methodDetails.unitType)
1861
1941
 
@@ -1868,6 +1948,7 @@ function getPinnedRequestBinding(request: Record<string, unknown>): PinnedReques
1868
1948
  methodBinding: {
1869
1949
  ...(chainId !== undefined ? { chainId } : {}),
1870
1950
  ...(memo !== undefined ? { memo } : {}),
1951
+ ...(sessionProtocol !== undefined ? { sessionProtocol } : {}),
1871
1952
  ...(splits !== undefined ? { splits } : {}),
1872
1953
  ...(unitType !== undefined ? { unitType } : {}),
1873
1954
  },
@@ -2021,7 +2102,7 @@ type ConfiguredHandler = ((input: Request) => Promise<MethodFn.Response<Transpor
2021
2102
  }
2022
2103
  }
2023
2104
 
2024
- const paymentAuthChallengeHeader = 'WWW-Authenticate'
2105
+ const paymentAuthChallengeHeader = Constants.Headers.wwwAuthenticate
2025
2106
 
2026
2107
  const challengeHeaderMerges = [
2027
2108
  {
@@ -2141,7 +2222,7 @@ export function compose(
2141
2222
  // Try to extract a Payment credential to decide whether to dispatch or challenge.
2142
2223
  // Only gate on the Payment scheme — other auth schemes (Bearer, Basic, etc.)
2143
2224
  // should fall through to the merged-402 path so all offers are presented.
2144
- const header = input.headers.get('Authorization')
2225
+ const header = input.headers.get(Constants.Headers.authorization)
2145
2226
  const paymentHeader = header ? Credential.extractPaymentScheme(header) : null
2146
2227
 
2147
2228
  if (paymentHeader) {
@@ -2222,7 +2303,7 @@ export function compose(
2222
2303
  })
2223
2304
  }
2224
2305
 
2225
- const acceptPayment = input.headers.get('Accept-Payment')
2306
+ const acceptPayment = input.headers.get(Constants.Headers.acceptPayment)
2226
2307
  if (!acceptPayment) return entries
2227
2308
 
2228
2309
  try {
@@ -2243,7 +2324,7 @@ export function compose(
2243
2324
  result.status === 402 ? [result.challenge as Response] : [],
2244
2325
  )
2245
2326
  const unnegotiatedX402Responses =
2246
- input.headers.has('Accept-Payment') || challengeEntries.length === 0
2327
+ input.headers.has(Constants.Headers.acceptPayment) || challengeEntries.length === 0
2247
2328
  ? []
2248
2329
  : challengeResponses.filter(
2249
2330
  (response) =>