mppx 0.8.14 → 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 (507) hide show
  1. package/CHANGELOG.md +57 -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/cli/validate/payment.d.ts.map +1 -1
  66. package/dist/cli/validate/payment.js +18 -4
  67. package/dist/cli/validate/payment.js.map +1 -1
  68. package/dist/client/Mppx.d.ts +3 -0
  69. package/dist/client/Mppx.d.ts.map +1 -1
  70. package/dist/client/Mppx.js +13 -5
  71. package/dist/client/Mppx.js.map +1 -1
  72. package/dist/client/Transport.d.ts +3 -5
  73. package/dist/client/Transport.d.ts.map +1 -1
  74. package/dist/client/Transport.js +1 -17
  75. package/dist/client/Transport.js.map +1 -1
  76. package/dist/client/internal/Fetch.d.ts.map +1 -1
  77. package/dist/client/internal/Fetch.js +92 -13
  78. package/dist/client/internal/Fetch.js.map +1 -1
  79. package/dist/client/internal/MethodChallenge.d.ts +2 -0
  80. package/dist/client/internal/MethodChallenge.d.ts.map +1 -1
  81. package/dist/client/internal/MethodChallenge.js +4 -0
  82. package/dist/client/internal/MethodChallenge.js.map +1 -1
  83. package/dist/client/internal/MethodResponse.d.ts +15 -0
  84. package/dist/client/internal/MethodResponse.d.ts.map +1 -1
  85. package/dist/client/internal/MethodResponse.js +10 -0
  86. package/dist/client/internal/MethodResponse.js.map +1 -1
  87. package/dist/client/node.js.map +1 -1
  88. package/dist/discovery/OpenApi.d.ts +3 -5
  89. package/dist/discovery/OpenApi.d.ts.map +1 -1
  90. package/dist/discovery/OpenApi.js +13 -34
  91. package/dist/discovery/OpenApi.js.map +1 -1
  92. package/dist/discovery/internal/Metadata.d.ts +20 -0
  93. package/dist/discovery/internal/Metadata.d.ts.map +1 -0
  94. package/dist/discovery/internal/Metadata.js +36 -0
  95. package/dist/discovery/internal/Metadata.js.map +1 -0
  96. package/dist/evm/Methods.d.ts +11 -11
  97. package/dist/evm/Methods.d.ts.map +1 -1
  98. package/dist/evm/Types.d.ts +17 -17
  99. package/dist/evm/Types.d.ts.map +1 -1
  100. package/dist/evm/client/Charge.d.ts +11 -11
  101. package/dist/evm/client/Methods.d.ts +11 -11
  102. package/dist/evm/server/Charge.d.ts +1 -1
  103. package/dist/evm/server/Charge.d.ts.map +1 -1
  104. package/dist/evm/server/Charge.js +2 -0
  105. package/dist/evm/server/Charge.js.map +1 -1
  106. package/dist/evm/server/Methods.d.ts +11 -11
  107. package/dist/internal/HeaderCodec.d.ts +2 -2
  108. package/dist/internal/HeaderCodec.d.ts.map +1 -1
  109. package/dist/internal/types.d.ts +6 -0
  110. package/dist/internal/types.d.ts.map +1 -1
  111. package/dist/middlewares/elysia.js.map +1 -1
  112. package/dist/middlewares/express.js.map +1 -1
  113. package/dist/middlewares/hono.js.map +1 -1
  114. package/dist/middlewares/nextjs.js.map +1 -1
  115. package/dist/proxy/Proxy.d.ts +2 -0
  116. package/dist/proxy/Proxy.d.ts.map +1 -1
  117. package/dist/proxy/Proxy.js +117 -9
  118. package/dist/proxy/Proxy.js.map +1 -1
  119. package/dist/proxy/Service.d.ts +36 -1
  120. package/dist/proxy/Service.d.ts.map +1 -1
  121. package/dist/proxy/Service.js +9 -8
  122. package/dist/proxy/Service.js.map +1 -1
  123. package/dist/proxy/services/anthropic.d.ts.map +1 -1
  124. package/dist/proxy/services/anthropic.js +1 -0
  125. package/dist/proxy/services/anthropic.js.map +1 -1
  126. package/dist/proxy/services/openai.d.ts.map +1 -1
  127. package/dist/proxy/services/openai.js +1 -0
  128. package/dist/proxy/services/openai.js.map +1 -1
  129. package/dist/proxy/services/stripe.d.ts.map +1 -1
  130. package/dist/proxy/services/stripe.js +1 -0
  131. package/dist/proxy/services/stripe.js.map +1 -1
  132. package/dist/server/Mppx.d.ts +75 -13
  133. package/dist/server/Mppx.d.ts.map +1 -1
  134. package/dist/server/Mppx.js +201 -23
  135. package/dist/server/Mppx.js.map +1 -1
  136. package/dist/server/internal/html/config.d.ts +9 -9
  137. package/dist/server/internal/html/config.d.ts.map +1 -1
  138. package/dist/server/internal/html/constants.d.ts +17 -17
  139. package/dist/server/internal/html/constants.d.ts.map +1 -1
  140. package/dist/stripe/Methods.d.ts +10 -10
  141. package/dist/stripe/Methods.d.ts.map +1 -1
  142. package/dist/stripe/client/Charge.d.ts +10 -10
  143. package/dist/stripe/client/Methods.d.ts +10 -10
  144. package/dist/stripe/internal/constants.d.ts +1 -1
  145. package/dist/stripe/internal/constants.js +1 -1
  146. package/dist/stripe/internal/types.d.ts +1 -0
  147. package/dist/stripe/internal/types.d.ts.map +1 -1
  148. package/dist/stripe/server/Charge.d.ts +11 -11
  149. package/dist/stripe/server/Charge.d.ts.map +1 -1
  150. package/dist/stripe/server/Charge.js +49 -1
  151. package/dist/stripe/server/Charge.js.map +1 -1
  152. package/dist/stripe/server/Methods.d.ts +137 -48
  153. package/dist/stripe/server/Methods.d.ts.map +1 -1
  154. package/dist/stripe/server/Methods.js +259 -4
  155. package/dist/stripe/server/Methods.js.map +1 -1
  156. package/dist/stripe/server/internal/deposit-address.d.ts +13 -0
  157. package/dist/stripe/server/internal/deposit-address.d.ts.map +1 -0
  158. package/dist/stripe/server/internal/deposit-address.js +28 -0
  159. package/dist/stripe/server/internal/deposit-address.js.map +1 -0
  160. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  161. package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
  162. package/dist/stripe/server/internal/html.gen.js +1 -1
  163. package/dist/stripe/server/internal/html.gen.js.map +1 -1
  164. package/dist/stripe/server/internal/record-payment.d.ts +15 -0
  165. package/dist/stripe/server/internal/record-payment.d.ts.map +1 -0
  166. package/dist/stripe/server/internal/record-payment.js +42 -0
  167. package/dist/stripe/server/internal/record-payment.js.map +1 -0
  168. package/dist/stripe/server/internal/request.d.ts +25 -0
  169. package/dist/stripe/server/internal/request.d.ts.map +1 -0
  170. package/dist/stripe/server/internal/request.js +29 -0
  171. package/dist/stripe/server/internal/request.js.map +1 -0
  172. package/dist/tap/Constants.d.ts +28 -0
  173. package/dist/tap/Constants.d.ts.map +1 -0
  174. package/dist/tap/Constants.js +32 -0
  175. package/dist/tap/Constants.js.map +1 -0
  176. package/dist/tap/Types.d.ts +11 -0
  177. package/dist/tap/Types.d.ts.map +1 -0
  178. package/dist/tap/Types.js +2 -0
  179. package/dist/tap/Types.js.map +1 -0
  180. package/dist/tap/client/index.d.ts +26 -0
  181. package/dist/tap/client/index.d.ts.map +1 -0
  182. package/dist/tap/client/index.js +33 -0
  183. package/dist/tap/client/index.js.map +1 -0
  184. package/dist/tap/index.d.ts +5 -0
  185. package/dist/tap/index.d.ts.map +1 -0
  186. package/dist/tap/index.js +5 -0
  187. package/dist/tap/index.js.map +1 -0
  188. package/dist/tap/server/index.d.ts +20 -0
  189. package/dist/tap/server/index.d.ts.map +1 -0
  190. package/dist/tap/server/index.js +36 -0
  191. package/dist/tap/server/index.js.map +1 -0
  192. package/dist/tempo/Attribution.d.ts +1 -1
  193. package/dist/tempo/Methods.d.ts +60 -60
  194. package/dist/tempo/Methods.d.ts.map +1 -1
  195. package/dist/tempo/Proof.d.ts +16 -16
  196. package/dist/tempo/client/Charge.d.ts +19 -19
  197. package/dist/tempo/client/Charge.d.ts.map +1 -1
  198. package/dist/tempo/client/Charge.js +1 -7
  199. package/dist/tempo/client/Charge.js.map +1 -1
  200. package/dist/tempo/client/Methods.d.ts +38 -38
  201. package/dist/tempo/client/Methods.js.map +1 -1
  202. package/dist/tempo/client/Subscription.d.ts +23 -23
  203. package/dist/tempo/client/Subscription.js.map +1 -1
  204. package/dist/tempo/internal/address.d.ts +2 -1
  205. package/dist/tempo/internal/address.d.ts.map +1 -1
  206. package/dist/tempo/internal/address.js +3 -1
  207. package/dist/tempo/internal/address.js.map +1 -1
  208. package/dist/tempo/internal/auto-swap.d.ts.map +1 -1
  209. package/dist/tempo/internal/auto-swap.js +4 -19
  210. package/dist/tempo/internal/auto-swap.js.map +1 -1
  211. package/dist/tempo/internal/defaults.d.ts +6 -6
  212. package/dist/tempo/internal/defaults.d.ts.map +1 -1
  213. package/dist/tempo/internal/fee-payer.d.ts +12 -12
  214. package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
  215. package/dist/tempo/internal/fee-payer.js.map +1 -1
  216. package/dist/tempo/internal/proof.d.ts +17 -17
  217. package/dist/tempo/internal/proof.d.ts.map +1 -1
  218. package/dist/tempo/legacy/client/Session.d.ts +20 -20
  219. package/dist/tempo/legacy/client/Session.js.map +1 -1
  220. package/dist/tempo/legacy/client/SessionManager.js.map +1 -1
  221. package/dist/tempo/legacy/index.d.ts +1 -3
  222. package/dist/tempo/legacy/index.d.ts.map +1 -1
  223. package/dist/tempo/legacy/index.js +1 -3
  224. package/dist/tempo/legacy/index.js.map +1 -1
  225. package/dist/tempo/legacy/session/escrow.abi.d.ts +374 -374
  226. package/dist/tempo/legacy/session/escrow.abi.d.ts.map +1 -1
  227. package/dist/tempo/server/Charge.d.ts +34 -12
  228. package/dist/tempo/server/Charge.d.ts.map +1 -1
  229. package/dist/tempo/server/Charge.js +44 -39
  230. package/dist/tempo/server/Charge.js.map +1 -1
  231. package/dist/tempo/server/Methods.d.ts +37 -52
  232. package/dist/tempo/server/Methods.d.ts.map +1 -1
  233. package/dist/tempo/server/Methods.js +0 -14
  234. package/dist/tempo/server/Methods.js.map +1 -1
  235. package/dist/tempo/server/Relay.d.ts +14 -5
  236. package/dist/tempo/server/Relay.d.ts.map +1 -1
  237. package/dist/tempo/server/Relay.js +6 -4
  238. package/dist/tempo/server/Relay.js.map +1 -1
  239. package/dist/tempo/server/SponsorBudget.d.ts.map +1 -1
  240. package/dist/tempo/server/SponsorBudget.js +7 -1
  241. package/dist/tempo/server/SponsorBudget.js.map +1 -1
  242. package/dist/tempo/server/Subscription.d.ts +25 -25
  243. package/dist/tempo/server/Subscription.d.ts.map +1 -1
  244. package/dist/tempo/server/Subscription.js +2 -0
  245. package/dist/tempo/server/Subscription.js.map +1 -1
  246. package/dist/tempo/server/index.d.ts +1 -1
  247. package/dist/tempo/server/index.d.ts.map +1 -1
  248. package/dist/tempo/server/index.js +1 -1
  249. package/dist/tempo/server/index.js.map +1 -1
  250. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  251. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
  252. package/dist/tempo/server/internal/html.gen.js +1 -1
  253. package/dist/tempo/server/internal/html.gen.js.map +1 -1
  254. package/dist/tempo/server/internal/transport.d.ts +2 -0
  255. package/dist/tempo/server/internal/transport.d.ts.map +1 -1
  256. package/dist/tempo/server/internal/transport.js +9 -2
  257. package/dist/tempo/server/internal/transport.js.map +1 -1
  258. package/dist/tempo/session/client/ChannelOps.js.map +1 -1
  259. package/dist/tempo/session/client/CredentialState.d.ts +1 -1
  260. package/dist/tempo/session/client/CredentialState.js.map +1 -1
  261. package/dist/tempo/session/client/ReceiptCoordinator.js.map +1 -1
  262. package/dist/tempo/session/client/Runtime.js.map +1 -1
  263. package/dist/tempo/session/client/Session.d.ts +19 -19
  264. package/dist/tempo/session/client/Session.d.ts.map +1 -1
  265. package/dist/tempo/session/client/Session.js +128 -14
  266. package/dist/tempo/session/client/Session.js.map +1 -1
  267. package/dist/tempo/session/client/SessionManager.d.ts.map +1 -1
  268. package/dist/tempo/session/client/SessionManager.js +62 -20
  269. package/dist/tempo/session/client/SessionManager.js.map +1 -1
  270. package/dist/tempo/session/client/Transports.d.ts +9 -1
  271. package/dist/tempo/session/client/Transports.d.ts.map +1 -1
  272. package/dist/tempo/session/client/Transports.js +36 -13
  273. package/dist/tempo/session/client/Transports.js.map +1 -1
  274. package/dist/tempo/session/precompile/Chain.d.ts +27 -0
  275. package/dist/tempo/session/precompile/Chain.d.ts.map +1 -1
  276. package/dist/tempo/session/precompile/Chain.js +68 -7
  277. package/dist/tempo/session/precompile/Chain.js.map +1 -1
  278. package/dist/tempo/session/precompile/Protocol.js.map +1 -1
  279. package/dist/tempo/session/precompile/Voucher.d.ts +4 -4
  280. package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -1
  281. package/dist/tempo/session/precompile/escrow.abi.d.ts +306 -306
  282. package/dist/tempo/session/precompile/escrow.abi.d.ts.map +1 -1
  283. package/dist/tempo/session/server/ChannelStore.d.ts +9 -0
  284. package/dist/tempo/session/server/ChannelStore.d.ts.map +1 -1
  285. package/dist/tempo/session/server/ChannelStore.js +16 -8
  286. package/dist/tempo/session/server/ChannelStore.js.map +1 -1
  287. package/dist/tempo/session/server/CredentialVerification.d.ts +30 -13
  288. package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -1
  289. package/dist/tempo/session/server/CredentialVerification.js +207 -13
  290. package/dist/tempo/session/server/CredentialVerification.js.map +1 -1
  291. package/dist/tempo/session/server/MeteredStream.d.ts +3 -0
  292. package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -1
  293. package/dist/tempo/session/server/MeteredStream.js +3 -1
  294. package/dist/tempo/session/server/MeteredStream.js.map +1 -1
  295. package/dist/tempo/session/server/Session.d.ts +32 -21
  296. package/dist/tempo/session/server/Session.d.ts.map +1 -1
  297. package/dist/tempo/session/server/Session.js +116 -53
  298. package/dist/tempo/session/server/Session.js.map +1 -1
  299. package/dist/tempo/session/server/Sse.d.ts +3 -1
  300. package/dist/tempo/session/server/Sse.d.ts.map +1 -1
  301. package/dist/tempo/session/server/Sse.js +2 -1
  302. package/dist/tempo/session/server/Sse.js.map +1 -1
  303. package/dist/tempo/session/server/Transports.d.ts +1 -1
  304. package/dist/tempo/session/server/Transports.d.ts.map +1 -1
  305. package/dist/tempo/session/server/Transports.js +1 -0
  306. package/dist/tempo/session/server/Transports.js.map +1 -1
  307. package/dist/tempo/session/server/Ws.d.ts +6 -1
  308. package/dist/tempo/session/server/Ws.d.ts.map +1 -1
  309. package/dist/tempo/session/server/Ws.js +4 -0
  310. package/dist/tempo/session/server/Ws.js.map +1 -1
  311. package/dist/tempo/subscription/KeyAuthorization.d.ts +38 -866
  312. package/dist/tempo/subscription/KeyAuthorization.d.ts.map +1 -1
  313. package/dist/tempo/subscription/KeyAuthorization.js +14 -3
  314. package/dist/tempo/subscription/KeyAuthorization.js.map +1 -1
  315. package/dist/viem/Account.d.ts.map +1 -1
  316. package/dist/viem/Account.js.map +1 -1
  317. package/dist/viem/Client.d.ts.map +1 -1
  318. package/dist/viem/Client.js +6 -1
  319. package/dist/viem/Client.js.map +1 -1
  320. package/dist/web-bot-auth/Constants.d.ts +20 -0
  321. package/dist/web-bot-auth/Constants.d.ts.map +1 -0
  322. package/dist/web-bot-auth/Constants.js +24 -0
  323. package/dist/web-bot-auth/Constants.js.map +1 -0
  324. package/dist/web-bot-auth/Types.d.ts +10 -0
  325. package/dist/web-bot-auth/Types.d.ts.map +1 -0
  326. package/dist/web-bot-auth/Types.js +2 -0
  327. package/dist/web-bot-auth/Types.js.map +1 -0
  328. package/dist/web-bot-auth/client/index.d.ts +29 -0
  329. package/dist/web-bot-auth/client/index.d.ts.map +1 -0
  330. package/dist/web-bot-auth/client/index.js +64 -0
  331. package/dist/web-bot-auth/client/index.js.map +1 -0
  332. package/dist/web-bot-auth/index.d.ts +7 -0
  333. package/dist/web-bot-auth/index.d.ts.map +1 -0
  334. package/dist/web-bot-auth/index.js +7 -0
  335. package/dist/web-bot-auth/index.js.map +1 -0
  336. package/dist/web-bot-auth/internal/JwkThumbprint.d.ts +5 -0
  337. package/dist/web-bot-auth/internal/JwkThumbprint.d.ts.map +1 -0
  338. package/dist/web-bot-auth/internal/JwkThumbprint.js +20 -0
  339. package/dist/web-bot-auth/internal/JwkThumbprint.js.map +1 -0
  340. package/dist/web-bot-auth/internal/SignatureAgent.d.ts +3 -0
  341. package/dist/web-bot-auth/internal/SignatureAgent.d.ts.map +1 -0
  342. package/dist/web-bot-auth/internal/SignatureAgent.js +18 -0
  343. package/dist/web-bot-auth/internal/SignatureAgent.js.map +1 -0
  344. package/dist/web-bot-auth/server/index.d.ts +35 -0
  345. package/dist/web-bot-auth/server/index.d.ts.map +1 -0
  346. package/dist/web-bot-auth/server/index.js +88 -0
  347. package/dist/web-bot-auth/server/index.js.map +1 -0
  348. package/dist/x402/Assets.d.ts.map +1 -1
  349. package/dist/x402/Types.d.ts +6 -6
  350. package/dist/x402/Types.d.ts.map +1 -1
  351. package/dist/x402/server/EvmCharge.d.ts.map +1 -1
  352. package/dist/x402/server/EvmCharge.js +32 -20
  353. package/dist/x402/server/EvmCharge.js.map +1 -1
  354. package/package.json +20 -4
  355. package/src/Challenge.fuzz.test.ts +80 -47
  356. package/src/Challenge.test.ts +65 -0
  357. package/src/Challenge.ts +39 -35
  358. package/src/Method.ts +82 -68
  359. package/src/Store.test-d.ts +16 -0
  360. package/src/Store.test.ts +50 -0
  361. package/src/Store.ts +68 -0
  362. package/src/attestation/Attestation.integration.test.ts +208 -0
  363. package/src/attestation/Client.test.ts +83 -0
  364. package/src/attestation/Client.ts +34 -0
  365. package/src/attestation/Constants.ts +15 -0
  366. package/src/attestation/Server.test.ts +60 -0
  367. package/src/attestation/Server.ts +12 -0
  368. package/src/attestation/Types.test-d.ts +54 -0
  369. package/src/attestation/Types.ts +77 -0
  370. package/src/attestation/index.ts +5 -0
  371. package/src/attestation/internal/HttpMessageSignature.test.ts +101 -0
  372. package/src/attestation/internal/HttpMessageSignature.ts +429 -0
  373. package/src/attestation/internal/SigningContext.ts +13 -0
  374. package/src/bin.js +2 -0
  375. package/src/cli/cli.ts +1 -0
  376. package/src/cli/validate/payment.ts +18 -4
  377. package/src/cli/validate.test.ts +16 -0
  378. package/src/client/Mppx.test-d.ts +17 -0
  379. package/src/client/Mppx.test.ts +14 -14
  380. package/src/client/Mppx.ts +25 -4
  381. package/src/client/Transport.test.ts +12 -45
  382. package/src/client/Transport.ts +3 -24
  383. package/src/client/internal/Fetch.test.ts +77 -0
  384. package/src/client/internal/Fetch.ts +112 -14
  385. package/src/client/internal/MethodChallenge.ts +5 -0
  386. package/src/client/internal/MethodResponse.ts +28 -0
  387. package/src/discovery/OpenApi.test.ts +170 -0
  388. package/src/discovery/OpenApi.ts +16 -48
  389. package/src/discovery/internal/Metadata.ts +51 -0
  390. package/src/evm/server/Charge.ts +6 -1
  391. package/src/internal/AcceptPayment.test.ts +12 -8
  392. package/src/internal/types.ts +9 -0
  393. package/src/mcp/client/McpClient.integration.test.ts +19 -32
  394. package/src/middlewares/elysia.test.ts +2 -12
  395. package/src/middlewares/express.test.ts +2 -12
  396. package/src/middlewares/hono.test.ts +6 -9
  397. package/src/middlewares/nextjs.test.ts +2 -12
  398. package/src/proxy/Proxy.test.ts +26 -19
  399. package/src/proxy/Proxy.ts +147 -9
  400. package/src/proxy/Proxy.upstreamError.test.ts +364 -0
  401. package/src/proxy/Service.test.ts +73 -3
  402. package/src/proxy/Service.ts +50 -23
  403. package/src/proxy/services/anthropic.ts +1 -0
  404. package/src/proxy/services/hooks.test-d.ts +14 -0
  405. package/src/proxy/services/hooks.test.ts +44 -0
  406. package/src/proxy/services/openai.ts +1 -0
  407. package/src/proxy/services/stripe.ts +1 -0
  408. package/src/server/Methods.test-d.ts +88 -0
  409. package/src/server/Methods.test.ts +69 -0
  410. package/src/server/Mppx.test-d.ts +141 -7
  411. package/src/server/Mppx.test.ts +1017 -119
  412. package/src/server/Mppx.ts +350 -54
  413. package/src/stripe/internal/constants.ts +1 -1
  414. package/src/stripe/internal/types.ts +1 -0
  415. package/src/stripe/server/Charge.test-d.ts +16 -1
  416. package/src/stripe/server/Charge.test.ts +79 -0
  417. package/src/stripe/server/Charge.ts +54 -1
  418. package/src/stripe/server/Methods.test-d.ts +157 -0
  419. package/src/stripe/server/Methods.test.ts +403 -0
  420. package/src/stripe/server/Methods.ts +423 -6
  421. package/src/stripe/server/internal/deposit-address.ts +57 -0
  422. package/src/stripe/server/internal/html/node_modules/.bin/mppx +3 -3
  423. package/src/stripe/server/internal/html/package.json +1 -1
  424. package/src/stripe/server/internal/html.gen.ts +1 -1
  425. package/src/stripe/server/internal/record-payment.ts +66 -0
  426. package/src/stripe/server/internal/request.ts +49 -0
  427. package/src/tap/Constants.ts +32 -0
  428. package/src/tap/Tap.test.ts +125 -0
  429. package/src/tap/Types.ts +11 -0
  430. package/src/tap/client/index.ts +54 -0
  431. package/src/tap/index.ts +4 -0
  432. package/src/tap/server/index.ts +48 -0
  433. package/src/tempo/PublicExports.test-d.ts +3 -3
  434. package/src/tempo/client/Charge.ts +1 -19
  435. package/src/tempo/internal/address.test.ts +42 -0
  436. package/src/tempo/internal/address.ts +3 -3
  437. package/src/tempo/internal/auto-swap.test.ts +4 -4
  438. package/src/tempo/internal/auto-swap.ts +7 -50
  439. package/src/tempo/legacy/README.md +5 -5
  440. package/src/tempo/legacy/index.ts +1 -3
  441. package/src/tempo/server/AtomicStore.test-d.ts +13 -0
  442. package/src/tempo/server/Charge.test.ts +19 -84
  443. package/src/tempo/server/Charge.ts +92 -56
  444. package/src/tempo/server/Methods.ts +0 -20
  445. package/src/tempo/server/Relay.test.ts +161 -7
  446. package/src/tempo/server/Relay.ts +15 -6
  447. package/src/tempo/server/SponsorBudget.test.ts +29 -0
  448. package/src/tempo/server/SponsorBudget.ts +7 -1
  449. package/src/tempo/server/Subscription.ts +4 -1
  450. package/src/tempo/server/index.ts +1 -1
  451. package/src/tempo/server/internal/html/node_modules/.bin/mppx +3 -3
  452. package/src/tempo/server/internal/html/package.json +1 -1
  453. package/src/tempo/server/internal/html.gen.ts +1 -1
  454. package/src/tempo/server/internal/transport.test.ts +22 -3
  455. package/src/tempo/server/internal/transport.ts +15 -3
  456. package/src/tempo/session/README.md +119 -0
  457. package/src/tempo/session/client/Session.test.ts +299 -33
  458. package/src/tempo/session/client/Session.ts +152 -16
  459. package/src/tempo/session/client/SessionManager.test.ts +216 -9
  460. package/src/tempo/session/client/SessionManager.ts +63 -17
  461. package/src/tempo/session/client/Transports.test.ts +22 -0
  462. package/src/tempo/session/client/Transports.ts +58 -22
  463. package/src/tempo/session/precompile/Chain.ts +131 -11
  464. package/src/tempo/session/server/ChannelStore.ts +33 -12
  465. package/src/tempo/session/server/CredentialVerification.test.ts +4 -4
  466. package/src/tempo/session/server/CredentialVerification.ts +340 -28
  467. package/src/tempo/session/server/MeteredStream.ts +5 -1
  468. package/src/tempo/session/server/Session.test.ts +483 -36
  469. package/src/tempo/session/server/Session.ts +159 -60
  470. package/src/tempo/session/server/Sse.test.ts +77 -0
  471. package/src/tempo/session/server/Sse.ts +8 -1
  472. package/src/tempo/session/server/Transports.ts +2 -1
  473. package/src/tempo/session/server/Ws.test.ts +171 -0
  474. package/src/tempo/session/server/Ws.ts +10 -1
  475. package/src/tempo/subscription/KeyAuthorization.test.ts +70 -1
  476. package/src/tempo/subscription/KeyAuthorization.ts +14 -3
  477. package/src/viem/Client.test.ts +18 -1
  478. package/src/viem/Client.ts +7 -1
  479. package/src/web-bot-auth/Constants.ts +24 -0
  480. package/src/web-bot-auth/Types.ts +9 -0
  481. package/src/web-bot-auth/WebBotAuth.test.ts +182 -0
  482. package/src/web-bot-auth/client/index.ts +84 -0
  483. package/src/web-bot-auth/index.ts +6 -0
  484. package/src/web-bot-auth/internal/JwkThumbprint.ts +21 -0
  485. package/src/web-bot-auth/internal/SignatureAgent.ts +18 -0
  486. package/src/web-bot-auth/server/index.ts +113 -0
  487. package/src/x402/Exact.e2e.test.ts +121 -81
  488. package/src/x402/Exact.localnet.test.ts +1 -1
  489. package/src/x402/server/EvmCharge.ts +35 -19
  490. package/dist/tempo/legacy/server/Session.d.ts +0 -208
  491. package/dist/tempo/legacy/server/Session.d.ts.map +0 -1
  492. package/dist/tempo/legacy/server/Session.js +0 -698
  493. package/dist/tempo/legacy/server/Session.js.map +0 -1
  494. package/dist/tempo/legacy/server/index.d.ts +0 -5
  495. package/dist/tempo/legacy/server/index.d.ts.map +0 -1
  496. package/dist/tempo/legacy/server/index.js +0 -5
  497. package/dist/tempo/legacy/server/index.js.map +0 -1
  498. package/dist/tempo/legacy/session/ChannelStore.d.ts +0 -22
  499. package/dist/tempo/legacy/session/ChannelStore.d.ts.map +0 -1
  500. package/dist/tempo/legacy/session/ChannelStore.js +0 -6
  501. package/dist/tempo/legacy/session/ChannelStore.js.map +0 -1
  502. package/src/tempo/legacy/server/Defaults.test-d.ts +0 -9
  503. package/src/tempo/legacy/server/Session.test.ts +0 -6370
  504. package/src/tempo/legacy/server/Session.ts +0 -1024
  505. package/src/tempo/legacy/server/index.ts +0 -4
  506. package/src/tempo/legacy/session/ChannelStore.test.ts +0 -58
  507. package/src/tempo/legacy/session/ChannelStore.ts +0 -39
package/src/Method.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  import type * as Challenge from './Challenge.js'
2
- import * as Constants from './Constants.js'
3
2
  import * as Credential from './Credential.js'
4
3
  import * as Errors from './Errors.js'
5
4
  import * as Expires from './Expires.js'
6
- import type { ExactPartial, LooseOmit, MaybePromise } from './internal/types.js'
5
+ import type { DeepReadonly, ExactPartial, LooseOmit, MaybePromise } from './internal/types.js'
7
6
  import type * as Receipt from './Receipt.js'
8
7
  import type * as Html from './server/internal/html/config.js'
9
8
  import type * as Transport from './server/Transport.js'
@@ -195,28 +194,64 @@ export type Server<
195
194
  transportOverride = undefined,
196
195
  extensions extends object = {},
197
196
  alias extends string | undefined = string | undefined,
198
- > = method & {
199
- alias?: alias | undefined
200
- authorize?: AuthorizeFn<method> | undefined
201
- defaults?: defaults | undefined
202
- extensions?: extensions | undefined
203
- html?: Html.Options | undefined
204
- preflight?: PreflightFn<method> | undefined
205
- request?: RequestFn<method> | undefined
206
- respond?: RespondFn<method> | undefined
207
- broadcast?: BroadcastFn<method> | undefined
208
- stableBinding?: StableBindingFn<method> | undefined
209
- transport?: transportOverride | undefined
210
- validate?: ValidateFn<method> | undefined
211
- /**
212
- * @deprecated Use `validate` for the non-mutating pre-check and `broadcast`
213
- * for settlement. `verify` combines both operations and may consume payment
214
- * state, so it cannot support a safe pre-check endpoint.
215
- */
216
- verify: VerifyFn<method>
217
- }
197
+ > = method &
198
+ ComposableHooks<method> & {
199
+ alias?: alias | undefined
200
+ authorize?: AuthorizeFn<method> | undefined
201
+ defaults?: defaults | undefined
202
+ extensions?: extensions | undefined
203
+ html?: Html.Options | undefined
204
+ preflight?: PreflightFn<method> | undefined
205
+ request?: RequestFn<method> | undefined
206
+ respond?: RespondFn<method> | undefined
207
+ broadcast?: BroadcastFn<method> | undefined
208
+ stableBinding?: StableBindingFn<method> | undefined
209
+ transport?: transportOverride | undefined
210
+ validate?: ValidateFn<method> | undefined
211
+ /**
212
+ * @deprecated Use `validate` for the non-mutating pre-check and `broadcast`
213
+ * for settlement. `verify` combines both operations and may consume payment
214
+ * state, so it cannot support a safe pre-check endpoint.
215
+ */
216
+ verify: VerifyFn<method>
217
+ }
218
218
  export type AnyServer = Server<any, any, any, any, any>
219
219
 
220
+ /**
221
+ * Decides whether a configured method offer is available for an HTTP request.
222
+ *
223
+ * Called once per configured offer during composition. The payment request is
224
+ * schema-normalized and deeply immutable. `input` is cloned so its body can be
225
+ * safely inspected unless the upstream body was already consumed or locked, in
226
+ * which case the clone preserves request metadata without a body. Returning
227
+ * `false` removes only this method's offer. Successfully matched credential
228
+ * dispatch does not invoke this hook.
229
+ */
230
+ export type CanOfferFn<method extends Method> = (parameters: {
231
+ input: globalThis.Request
232
+ request: DeepReadonly<z.output<method['schema']['request']>>
233
+ }) => MaybePromise<boolean>
234
+
235
+ /** Hooks supported by every composable server method constructor. */
236
+ /**
237
+ * Optional per-method hook invoked after a payment succeeds.
238
+ *
239
+ * Called after verification completes successfully. Use this for side-effects
240
+ * like recording the payment in an external system.
241
+ */
242
+ export type OnPaymentSuccessFn<method extends Method> = (parameters: {
243
+ input?: globalThis.Request
244
+ receipt: DeepReadonly<Receipt.Receipt>
245
+ request: DeepReadonly<z.output<method['schema']['request']>>
246
+ }) => MaybePromise<void>
247
+
248
+ export type ComposableHooks<method extends Method> = {
249
+ /** Decides whether this method's configured offer is available for an HTTP request. */
250
+ canOffer?: CanOfferFn<method> | undefined
251
+ /** Invoked after a successful payment on this method. */
252
+ onPaymentSuccess?: OnPaymentSuccessFn<method> | undefined
253
+ }
254
+
220
255
  /** Credential creation function for a single method. */
221
256
  export type CreateCredentialFn<method extends Method, context = unknown> = (
222
257
  parameters: {
@@ -363,11 +398,9 @@ export async function broadcastCredential<const methods extends readonly AnyServ
363
398
  /**
364
399
  * Parses a submitted credential into the inputs required for method execution.
365
400
  *
366
- * Dispatch is based on the challenge method and intent. When more than one
367
- * server method handles the same wire identity, session protocol details select
368
- * the appropriate implementation. The helper then asserts challenge expiry and
369
- * parses the method-specific credential payload before returning the selected
370
- * method and the unmodified challenge request.
401
+ * Dispatch is based on the challenge method and intent. The helper then asserts
402
+ * challenge expiry and parses the method-specific credential payload before
403
+ * returning the selected method and the unmodified challenge request.
371
404
  *
372
405
  * This intentionally does not verify that the challenge was issued by a
373
406
  * particular host, authorize the caller or requested resource, validate the
@@ -387,7 +420,7 @@ function prepareCredential(
387
420
  (method) =>
388
421
  method.name === credential.challenge.method && method.intent === credential.challenge.intent,
389
422
  )
390
- const method = selectServerMethod(candidates, credential.challenge)
423
+ const method = candidates[0]
391
424
  if (!method)
392
425
  throw new Errors.InvalidChallengeError({
393
426
  id: credential.challenge.id,
@@ -410,29 +443,6 @@ function prepareCredential(
410
443
  }
411
444
  }
412
445
 
413
- /** @internal */
414
- export function selectServerMethod(
415
- methods: readonly AnyServer[],
416
- challenge: Challenge.Challenge,
417
- ): AnyServer | undefined {
418
- if (methods.length <= 1) return methods[0]
419
- if (
420
- challenge.method !== Constants.Methods.tempo ||
421
- challenge.intent !== Constants.Intents.session
422
- )
423
- return methods[0]
424
-
425
- const sessionProtocol = Constants.getMethodDetail(
426
- challenge.request.methodDetails,
427
- Constants.MethodDetailKeys.sessionProtocol,
428
- )
429
- if (sessionProtocol === undefined || sessionProtocol === Constants.SessionProtocols.v1)
430
- return methods.find((method) => method.alias === 'sessionLegacy') ?? methods[0]
431
- if (sessionProtocol === Constants.SessionProtocols.v2)
432
- return methods.find((method) => method.alias === undefined) ?? methods[0]
433
- return undefined
434
- }
435
-
436
446
  /**
437
447
  * Optional respond function for a server-side method.
438
448
  *
@@ -537,9 +547,11 @@ export function toServer<
537
547
  const {
538
548
  alias,
539
549
  authorize,
550
+ canOffer,
540
551
  defaults,
541
552
  extensions,
542
553
  html,
554
+ onPaymentSuccess,
543
555
  preflight,
544
556
  request,
545
557
  respond,
@@ -563,9 +575,11 @@ export function toServer<
563
575
  ...method,
564
576
  alias,
565
577
  authorize,
578
+ canOffer,
566
579
  defaults,
567
580
  extensions,
568
581
  html,
582
+ onPaymentSuccess,
569
583
  preflight,
570
584
  request,
571
585
  respond,
@@ -586,7 +600,7 @@ export declare namespace toServer {
586
600
  transportOverride extends Transport.AnyTransport | undefined = undefined,
587
601
  extensions extends object = {},
588
602
  alias extends string | undefined = undefined,
589
- > = {
603
+ > = ComposableHooks<method> & {
590
604
  alias?: alias | undefined
591
605
  authorize?: AuthorizeFn<method> | undefined
592
606
  defaults?: defaults | undefined
@@ -598,20 +612,20 @@ export declare namespace toServer {
598
612
  stableBinding?: StableBindingFn<method> | undefined
599
613
  transport?: transportOverride | Transport.AnyTransport | undefined
600
614
  } & (
601
- | {
602
- broadcast: BroadcastFn<method>
603
- validate?: ValidateFn<method> | undefined
604
- verify?: undefined
605
- }
606
- | {
607
- broadcast?: undefined
608
- validate?: undefined
609
- /**
610
- * @deprecated Use `validate` and `broadcast` for new methods. This
611
- * combined hook may mutate payment state and cannot power a safe
612
- * validation-only endpoint.
613
- */
614
- verify: VerifyFn<method>
615
- }
616
- )
615
+ | {
616
+ broadcast: BroadcastFn<method>
617
+ validate?: ValidateFn<method> | undefined
618
+ verify?: undefined
619
+ }
620
+ | {
621
+ broadcast?: undefined
622
+ validate?: undefined
623
+ /**
624
+ * @deprecated Use `validate` and `broadcast` for new methods. This
625
+ * combined hook may mutate payment state and cannot power a safe
626
+ * validation-only endpoint.
627
+ */
628
+ verify: VerifyFn<method>
629
+ }
630
+ )
617
631
  }
@@ -20,6 +20,11 @@ test('default AtomicStore accepts any string key on update', () => {
20
20
  expectTypeOf(store.update).parameter(0).toBeString()
21
21
  })
22
22
 
23
+ test('default AtomicStore accepts any string key on tryClaim', () => {
24
+ const store = {} as Store.AtomicStore
25
+ expectTypeOf<NonNullable<typeof store.tryClaim>>().parameter(0).toBeString()
26
+ })
27
+
23
28
  test('memory returns AtomicStore', () => {
24
29
  const store = Store.memory()
25
30
  expectTypeOf(store).toEqualTypeOf<Store.AtomicStore>()
@@ -110,6 +115,17 @@ test('typed AtomicStore constrains keys on update', () => {
110
115
  expectTypeOf(store.update).parameter(0).toEqualTypeOf<`mppx:charge:${string}`>()
111
116
  })
112
117
 
118
+ test('typed tryClaim constrains keys', () => {
119
+ type ItemMap = { [key: `mppx:charge:${string}`]: number }
120
+ const store = {} as Store.AtomicStore<ItemMap>
121
+
122
+ expectTypeOf(Store.tryClaim(store, 'mppx:charge:0x123', 1)).toEqualTypeOf<
123
+ boolean | Promise<boolean>
124
+ >()
125
+ // @ts-expect-error — key must match the item map
126
+ Store.tryClaim(store, 'other:0x123', 1)
127
+ })
128
+
113
129
  test('typed Store infers value from key', async () => {
114
130
  type ItemMap = { [key: `mppx:charge:${string}`]: number }
115
131
  const store = {} as Store.Store<ItemMap>
package/src/Store.test.ts CHANGED
@@ -140,6 +140,17 @@ describe('keyPrefix', () => {
140
140
  expect(await backing.get('k')).toBeNull()
141
141
  })
142
142
 
143
+ test('scopes optimized tryClaim to prefixed backing keys', async () => {
144
+ const backing = Store.memory()
145
+ const store = Store.from(backing, { keyPrefix: 'tenant:' })
146
+ const expires = Date.now() + 60_000
147
+
148
+ expect(await Store.tryClaim(store, 'k', expires)).toBe(true)
149
+ expect(await Store.tryClaim(store, 'k', expires)).toBe(false)
150
+ expect(await backing.get('tenant:k')).toEqual({ expires, type: 'mppx:replay' })
151
+ expect(await backing.get('k')).toBeNull()
152
+ })
153
+
143
154
  test('returns the original store when keyPrefix is empty', () => {
144
155
  const backing = Store.memory()
145
156
  expect(Store.from(backing, { keyPrefix: '' })).toBe(backing)
@@ -189,6 +200,45 @@ describe('keyPrefix', () => {
189
200
  })
190
201
  })
191
202
 
203
+ describe('tryClaim', () => {
204
+ test('uses an adapter fast path when available', async () => {
205
+ const backing = Store.memory()
206
+ const optimized = backing.tryClaim!
207
+ let calls = 0
208
+ const store = {
209
+ ...backing,
210
+ tryClaim(key: string, expires: number) {
211
+ calls++
212
+ return optimized(key, expires)
213
+ },
214
+ }
215
+ const expires = Date.now() + 60_000
216
+
217
+ expect(await Store.tryClaim(store, 'k', expires)).toBe(true)
218
+ expect(await Store.tryClaim(store, 'k', expires)).toBe(false)
219
+ expect(await store.get('k')).toEqual({ expires, type: 'mppx:replay' })
220
+ expect(calls).toBe(2)
221
+ })
222
+
223
+ test('falls back to atomic update and replaces expired claims', async () => {
224
+ const store = Store.cloudflare(fakeStringKv())
225
+ expect(store.tryClaim).toBeUndefined()
226
+ const expires = Date.now() + 60_000
227
+
228
+ expect(await Store.tryClaim(store, 'k', Date.now() - 1)).toBe(true)
229
+ expect(await Store.tryClaim(store, 'k', expires)).toBe(true)
230
+ expect(await Store.tryClaim(store, 'k', expires)).toBe(false)
231
+ expect(await store.get('k')).toEqual({ expires, type: 'mppx:replay' })
232
+ })
233
+
234
+ test('preserves legacy replay markers as consumed', async () => {
235
+ const store = Store.memory()
236
+ await store.put('k', Date.now() - 60_000)
237
+
238
+ expect(await Store.tryClaim(store, 'k', Date.now() + 60_000)).toBe(false)
239
+ })
240
+ })
241
+
192
242
  describe('json roundtrip behavior', () => {
193
243
  test('cloudflare json-roundtrips nested objects', async () => {
194
244
  const store = Store.cloudflare(fakeStringKv())
package/src/Store.ts CHANGED
@@ -16,6 +16,8 @@
16
16
  */
17
17
  import { Json } from 'ox'
18
18
 
19
+ import type { MaybePromise } from './internal/types.js'
20
+
19
21
  export type StoreItemMap = Record<string, unknown>
20
22
 
21
23
  /**
@@ -47,6 +49,20 @@ export type Update<itemMap extends StoreItemMap = StoreItemMap> = <
47
49
  fn: (current: itemMap[key] | null) => Change<itemMap[key], result>,
48
50
  ) => Promise<result>
49
51
 
52
+ /** Attempts to atomically record first use of a replay key through its expiry. */
53
+ export type TryClaim<itemMap extends StoreItemMap = StoreItemMap> = <
54
+ key extends keyof itemMap & string,
55
+ >(
56
+ key: key,
57
+ expires: number,
58
+ ) => MaybePromise<boolean>
59
+
60
+ /** Persisted marker used by the {@link tryClaim} fallback. */
61
+ export type ReplayMarker = {
62
+ expires: number
63
+ type: 'mppx:replay'
64
+ }
65
+
50
66
  /** Base key-value actions available on every {@link Store}. */
51
67
  export type StoreActions<itemMap extends StoreItemMap = StoreItemMap> = {
52
68
  get: <key extends keyof itemMap & string>(key: key) => Promise<itemMap[key] | null>
@@ -57,6 +73,8 @@ export type StoreActions<itemMap extends StoreItemMap = StoreItemMap> = {
57
73
  /** Atomic actions that can be provided via the `extended` slot. */
58
74
  export type AtomicActions<itemMap extends StoreItemMap = StoreItemMap> = {
59
75
  update: Update<itemMap>
76
+ /** Optional single-operation fast path for recording expiring replay keys. */
77
+ tryClaim?: TryClaim<itemMap> | undefined
60
78
  }
61
79
 
62
80
  /**
@@ -84,6 +102,42 @@ export type AtomicStore<itemMap extends StoreItemMap = StoreItemMap> = Store<
84
102
  AtomicActions<itemMap>
85
103
  >
86
104
 
105
+ /**
106
+ * Attempts to atomically record first use of a replay key through its expiry.
107
+ *
108
+ * Returns `true` when this call recorded the key and `false` when it was already
109
+ * recorded and unexpired. `expires` is a Unix timestamp in milliseconds.
110
+ * Uses the adapter's optimized primitive when available and otherwise falls
111
+ * back to {@link AtomicStore.update}.
112
+ */
113
+ export function tryClaim<itemMap extends StoreItemMap, key extends keyof itemMap & string>(
114
+ store: AtomicStore<itemMap>,
115
+ key: key,
116
+ expires: number,
117
+ ): MaybePromise<boolean> {
118
+ if (store.tryClaim) return store.tryClaim(key, expires)
119
+ return store.update(key, (current) => {
120
+ if (current !== null && (!isReplayMarker(current) || current.expires > Date.now()))
121
+ return { op: 'noop', result: false }
122
+ return {
123
+ op: 'set',
124
+ value: { expires, type: 'mppx:replay' } as itemMap[key],
125
+ result: true,
126
+ }
127
+ })
128
+ }
129
+
130
+ function isReplayMarker(value: unknown): value is ReplayMarker {
131
+ return (
132
+ typeof value === 'object' &&
133
+ value !== null &&
134
+ 'expires' in value &&
135
+ typeof value.expires === 'number' &&
136
+ 'type' in value &&
137
+ value.type === 'mppx:replay'
138
+ )
139
+ }
140
+
87
141
  /** Options shared by store constructors. */
88
142
  export type Options = {
89
143
  /** Prefix prepended to every backing store key. */
@@ -127,6 +181,13 @@ function withKeyPrefix(store: Store | AtomicStore, keyPrefix = ''): Store | Atom
127
181
  },
128
182
  }
129
183
  : {}),
184
+ ...('tryClaim' in store && typeof store.tryClaim === 'function'
185
+ ? {
186
+ tryClaim(key: string, expires: number) {
187
+ return store.tryClaim!(prefixedKey(key), expires)
188
+ },
189
+ }
190
+ : {}),
130
191
  } satisfies Store | AtomicStore)
131
192
 
132
193
  const cachedByPrefix = keyPrefixCache.get(store) ?? new Map<string, Store | AtomicStore>()
@@ -226,6 +287,13 @@ export function memory(options?: memory.Options): AtomicStore {
226
287
  if (change.op === 'delete') store.delete(key)
227
288
  return change.result
228
289
  },
290
+ tryClaim(key: string, expires: number) {
291
+ const current = store.has(key) ? Json.parse(store.get(key)!) : null
292
+ if (current !== null && (!isReplayMarker(current) || current.expires > Date.now()))
293
+ return false
294
+ store.set(key, Json.stringify({ expires, type: 'mppx:replay' }))
295
+ return true
296
+ },
229
297
  },
230
298
  options,
231
299
  )
@@ -0,0 +1,208 @@
1
+ import type { RequestListener } from 'node:http'
2
+
3
+ import { Constants as MppConstants, Credential, Method, Receipt, z } from 'mppx'
4
+ import * as Attestation from 'mppx/attestation'
5
+ import * as Tap from 'mppx/attestation/tap'
6
+ import * as WebBotAuth from 'mppx/attestation/web-bot-auth'
7
+ import { Mppx as ClientMppx } from 'mppx/client'
8
+ import { Mppx, Request as ServerRequest } from 'mppx/server'
9
+ import { expect, test } from 'vp/test'
10
+ import { jwkThumbprint, keyPair } from '~test/Attestation.js'
11
+ import * as Http from '~test/Http.js'
12
+
13
+ const method = Method.from({
14
+ name: 'test',
15
+ intent: 'charge',
16
+ schema: {
17
+ credential: { payload: z.object({ token: z.string() }) },
18
+ request: z.object({ amount: z.string(), currency: z.string(), recipient: z.string() }),
19
+ },
20
+ })
21
+
22
+ const clientMethod = Method.toClient(method, {
23
+ async createCredential({ challenge }) {
24
+ return Credential.serialize({ challenge, payload: { token: 'paid' } })
25
+ },
26
+ })
27
+
28
+ const serverMethod = Method.toServer(method, {
29
+ async verify({ credential }) {
30
+ if (credential.payload.token !== 'paid') throw new Error('Payment credential is invalid.')
31
+ return Receipt.from({
32
+ method: 'test',
33
+ reference: 'test-payment',
34
+ status: 'success',
35
+ timestamp: new Date().toISOString(),
36
+ })
37
+ },
38
+ })
39
+
40
+ const charge = {
41
+ amount: '1',
42
+ currency: 'USD',
43
+ expires: new Date(Date.now() + 60_000).toISOString(),
44
+ recipient: 'merchant',
45
+ } as const
46
+
47
+ test('composes TAP and the Web Bot Auth directory profile across an MPP payment retry', async () => {
48
+ const tapKeys = await keyPair()
49
+ const webBotAuthKeys = await keyPair()
50
+ const webBotAuthKeyId = await jwkThumbprint(webBotAuthKeys.publicKey)
51
+ const signatureAgent = 'https://agent.example'
52
+ const nonceStore = Attestation.Store.memory()
53
+ const verifiers = {
54
+ tap: Tap.Server.verifier({
55
+ keyResolver: ({ keyId }) => (keyId === 'tap-agent' ? tapKeys.publicKey : undefined),
56
+ nonceStore,
57
+ }),
58
+ webBotAuth: WebBotAuth.Server.verifier({
59
+ keyResolver: ({ keyId, signatureAgent: advertisedAgent }) =>
60
+ keyId === webBotAuthKeyId && advertisedAgent === signatureAgent
61
+ ? webBotAuthKeys.publicKey
62
+ : undefined,
63
+ maxAge: 60,
64
+ nonceStore,
65
+ }),
66
+ }
67
+ const seen = await createServer(verifiers, async (request, payments) => {
68
+ const result = await payments.charge(charge)(request)
69
+ return result.status === 402 ? result.challenge : result.withReceipt(new Response('paid'))
70
+ })
71
+
72
+ try {
73
+ const client = ClientMppx.create({
74
+ attestation: {
75
+ tap: Tap.Client.signer({
76
+ intent: Tap.Constants.intents.payment,
77
+ key: tapKeys.privateKey,
78
+ keyId: 'tap-agent',
79
+ }),
80
+ webBotAuth: WebBotAuth.Client.signer({
81
+ key: webBotAuthKeys.privateKey,
82
+ keyId: webBotAuthKeyId,
83
+ signatureAgent,
84
+ }),
85
+ },
86
+ methods: [clientMethod],
87
+ polyfill: false,
88
+ })
89
+
90
+ const response = await client.fetch(seen.server.url)
91
+ expect(response.status).toBe(200)
92
+ expect(await response.text()).toBe('paid')
93
+ expect(seen.requests).toEqual([false, true])
94
+
95
+ const attemptNonces = seen.signatureInputs.map((input) =>
96
+ [...input.matchAll(/nonce="([^"]+)"/g)].map((match) => match[1]),
97
+ )
98
+ expect(attemptNonces).toHaveLength(2)
99
+ for (const nonces of attemptNonces) {
100
+ expect(nonces).toHaveLength(2)
101
+ expect(new Set(nonces).size).toBe(1)
102
+ expect(nonces[0]).toMatch(/^[A-Za-z0-9_-]{86}$/)
103
+ }
104
+ expect(attemptNonces[0]![0]).not.toBe(attemptNonces[1]![0])
105
+ } finally {
106
+ seen.server.close()
107
+ }
108
+ })
109
+
110
+ test('requires every configured server attestation', async () => {
111
+ const keys = await keyPair()
112
+ const seen = await createServer(
113
+ {
114
+ tap: Tap.Server.verifier({
115
+ keyResolver: () => keys.publicKey,
116
+ nonceStore: Attestation.Store.memory(),
117
+ }),
118
+ },
119
+ async (request, payments) => {
120
+ const result = await payments.charge(charge)(request)
121
+ return result.status === 402 ? result.challenge : result.withReceipt(new Response('paid'))
122
+ },
123
+ )
124
+
125
+ try {
126
+ const response = await fetch(seen.server.url)
127
+ expect(response.status).toBe(403)
128
+ expect(await response.text()).toMatch(/tap.*required/)
129
+ } finally {
130
+ seen.server.close()
131
+ }
132
+ })
133
+
134
+ test('rejects an invalid server attestation before creating a payment challenge', async () => {
135
+ const trustedKeys = await keyPair()
136
+ const untrustedKeys = await keyPair()
137
+ const seen = await createServer(
138
+ {
139
+ tap: Tap.Server.verifier({
140
+ keyResolver: () => trustedKeys.publicKey,
141
+ nonceStore: Attestation.Store.memory(),
142
+ }),
143
+ },
144
+ async (request, payments) => {
145
+ const result = await payments.charge(charge)(request)
146
+ return result.status === 402 ? result.challenge : result.withReceipt(new Response('paid'))
147
+ },
148
+ )
149
+
150
+ try {
151
+ const signer = Tap.Client.signer({
152
+ intent: Tap.Constants.intents.payment,
153
+ key: untrustedKeys.privateKey,
154
+ keyId: 'tap-agent',
155
+ })
156
+ const request = await signer.sign(new Request(seen.server.url))
157
+ const response = await fetch(request)
158
+ expect(response.status).toBe(401)
159
+ expect(await response.text()).toMatch(/tap.*invalid/)
160
+ expect(seen.requests).toEqual([false])
161
+ } finally {
162
+ seen.server.close()
163
+ }
164
+ })
165
+
166
+ test('rejects empty client and server attestation maps', () => {
167
+ expect(() =>
168
+ ClientMppx.create({
169
+ attestation: {},
170
+ methods: [clientMethod],
171
+ polyfill: false,
172
+ }),
173
+ ).toThrow('Mppx client attestation must configure at least one signer.')
174
+ expect(() =>
175
+ Mppx.create({
176
+ attestation: {},
177
+ methods: [serverMethod],
178
+ realm: 'localhost',
179
+ secretKey: 'test-secret-key-test-secret-key-32',
180
+ }),
181
+ ).toThrow('Mppx server attestation must configure at least one verifier.')
182
+ })
183
+
184
+ async function createServer(
185
+ verifiers: Attestation.VerifierMap,
186
+ handler: (request: Request, payments: ReturnType<typeof createPayments>) => Promise<Response>,
187
+ ) {
188
+ const payments = createPayments(verifiers)
189
+ const requests: boolean[] = []
190
+ const signatureInputs: string[] = []
191
+ const listener = ServerRequest.toNodeListener(async (request) => {
192
+ requests.push(request.headers.has(MppConstants.Headers.authorization))
193
+ signatureInputs.push(request.headers.get(Attestation.Headers.signatureInput) ?? '')
194
+ return handler(request, payments)
195
+ })
196
+ const server = await Http.createServer(((request, response) =>
197
+ listener(request, response)) as RequestListener)
198
+ return { requests, server, signatureInputs }
199
+ }
200
+
201
+ function createPayments(attestation: Attestation.VerifierMap) {
202
+ return Mppx.create({
203
+ attestation,
204
+ methods: [serverMethod],
205
+ realm: 'localhost',
206
+ secretKey: 'test-secret-key-test-secret-key-32',
207
+ })
208
+ }