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
@@ -1,6 +1,8 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http'
2
2
  import { isDeepStrictEqual } from 'node:util'
3
3
 
4
+ import * as AttestationServer from '../attestation/Server.js'
5
+ import type * as Attestation from '../attestation/Types.js'
4
6
  import * as Challenge from '../Challenge.js'
5
7
  import * as Constants from '../Constants.js'
6
8
  import * as Credential from '../Credential.js'
@@ -8,7 +10,7 @@ import * as Errors from '../Errors.js'
8
10
  import * as Expires from '../Expires.js'
9
11
  import * as AcceptPayment from '../internal/AcceptPayment.js'
10
12
  import * as Env from '../internal/env.js'
11
- import type { MaybePromise } from '../internal/types.js'
13
+ import type { DeepReadonly, MaybePromise } from '../internal/types.js'
12
14
  import * as Method from '../Method.js'
13
15
  import * as PaymentRequest from '../PaymentRequest.js'
14
16
  import type * as Receipt from '../Receipt.js'
@@ -85,6 +87,41 @@ export type ServerMethodDescriptor<
85
87
  name: method['name']
86
88
  }>
87
89
 
90
+ /** A configured payment offer exposed to the server's `selectOffers` hook. */
91
+ export type ServerOffer<method extends Method.Method = Method.Method> = method extends unknown
92
+ ? ServerOfferFor<method>
93
+ : never
94
+
95
+ type ServerOfferFor<method extends Method.Method> = Readonly<{
96
+ /** Canonical wire key for the configured method. */
97
+ key: `${method['name']}/${method['intent']}`
98
+ /** Immutable method metadata for this offer. */
99
+ method: ServerOfferMethodDescriptor<method>
100
+ /** Schema-normalized request configured for this offer. */
101
+ request: DeepReadonly<z.output<method['schema']['request']>>
102
+ }>
103
+
104
+ /** Immutable method metadata exposed to offer-selection hooks. */
105
+ export type ServerOfferMethodDescriptor<method extends Method.Method = Method.Method> = Readonly<{
106
+ /** Optional public alias used to register this server method. */
107
+ alias?: method extends { alias?: infer alias } ? alias : string
108
+ intent: method['intent']
109
+ name: method['name']
110
+ }>
111
+
112
+ /**
113
+ * Selects a subset of configured offers before payment challenges are issued.
114
+ *
115
+ * The hook must select at least one offer. Returned offers must come from the input,
116
+ * contain no duplicates, and preserve their original order. Offers and their
117
+ * normalized requests are deeply immutable. The incoming request is cloned so
118
+ * the hook may safely inspect its body when it has not already been consumed.
119
+ */
120
+ export type SelectOffers<methods extends readonly Method.Method[]> = (
121
+ offers: readonly ServerOffer<methods[number]>[],
122
+ context: Readonly<{ request: globalThis.Request }>,
123
+ ) => MaybePromise<readonly ServerOffer<methods[number]>[]>
124
+
88
125
  /** Context passed to `onChallengeCreated`. */
89
126
  export type ChallengeContext<
90
127
  method extends Method.Method = Method.Method,
@@ -227,9 +264,7 @@ export type Mppx<
227
264
  * })
228
265
  * ```
229
266
  */
230
- compose(
231
- ...entries: ComposeEntry<FlattenMethods<methods>>[]
232
- ): (input: Request) => Promise<MethodFn.Response<Transport.Http>>
267
+ compose(...entries: ComposeEntry<FlattenMethods<methods>>[]): ComposedHandler
233
268
  }
234
269
  : {}) &
235
270
  Handlers<FlattenMethods<methods>, transport> & {
@@ -339,22 +374,37 @@ type IsUniqueIntent<methods extends readonly Method.AnyServer[], intent extends
339
374
  : never
340
375
  : never
341
376
 
342
- /** Only includes shorthand intent keys when the intent is unique across methods. */
343
- type UniqueIntentHandlers<
377
+ /** Shorthand intent handlers. Single-method intents get direct handlers;
378
+ * multi-method intents get an implicit compose wrapper. */
379
+ type IntentHandlers<
344
380
  methods extends readonly Method.AnyServer[],
345
381
  transport extends Transport.AnyTransport,
346
382
  > = {
347
- [method_name in methods[number]['intent'] as IsUniqueIntent<methods, method_name> extends true
348
- ? method_name extends ReservedKey
349
- ? never
350
- : method_name
351
- : never]: MethodFn<
352
- Extract<methods[number], { intent: method_name }>,
353
- EffectiveTransportOf<Extract<methods[number], { intent: method_name }>, transport>,
354
- NonNullable<Extract<methods[number], { intent: method_name }>['defaults']>
355
- > &
356
- MethodExtensions<Extract<methods[number], { intent: method_name }>>
357
- }
383
+ [intent in methods[number]['intent'] as intent extends ReservedKey
384
+ ? never
385
+ : intent]: IsUniqueIntent<methods, intent> extends true
386
+ ? MethodFn<
387
+ Extract<methods[number], { intent: intent }>,
388
+ EffectiveTransportOf<Extract<methods[number], { intent: intent }>, transport>,
389
+ NonNullable<Extract<methods[number], { intent: intent }>['defaults']>
390
+ > &
391
+ MethodExtensions<Extract<methods[number], { intent: intent }>>
392
+ : MultiMethodFn<Extract<methods[number], { intent: intent }>, transport>
393
+ }
394
+
395
+ /** Handler for an intent with multiple methods - takes the intersection of all methods' options. */
396
+ type MultiMethodFn<methods extends Method.AnyServer, transport extends Transport.AnyTransport> = (
397
+ options: UnionToIntersection<MultiMethodFnDistribute<methods>>,
398
+ ) => (input: Transport.InputOf<transport>) => Promise<MethodFn.Response<transport>>
399
+
400
+ type MultiMethodFnDistribute<M> =
401
+ M extends Method.Server<infer method, infer defaults, any, any, any>
402
+ ? MethodFn.Options<method, NonNullable<defaults>>
403
+ : never
404
+
405
+ type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void
406
+ ? I
407
+ : never
358
408
 
359
409
  /** Nested handlers: `mppx.tempo.charge(...)`, grouped by method name then intent. */
360
410
  type PublicAlias<method extends Method.AnyServer> = method extends {
@@ -399,7 +449,7 @@ type Handlers<
399
449
  NonNullable<mi['defaults']>
400
450
  > &
401
451
  MethodExtensions<mi>
402
- } & UniqueIntentHandlers<methods, transport> &
452
+ } & IntentHandlers<methods, transport> &
403
453
  NestedHandlers<methods, transport> & {
404
454
  [mi in methods[number] as PublicAlias<mi> extends string
405
455
  ? `${mi['name']}/${PublicAlias<mi>}`
@@ -451,7 +501,9 @@ export function create<
451
501
  const transport extends Transport.AnyTransport = Transport.Http,
452
502
  >(config: create.Config<methods, transport>): Mppx<methods, transport> {
453
503
  const {
504
+ attestation,
454
505
  realm = Env.get('realm'),
506
+ selectOffers,
455
507
  secretKey = Env.get('secretKey'),
456
508
  transport = Transport.http() as transport,
457
509
  } = config
@@ -465,17 +517,32 @@ export function create<
465
517
 
466
518
  const methods = config.methods.flat() as unknown as FlattenMethods<methods>
467
519
  const serverEvents = createServerEventDispatcher<FlattenMethods<methods>, transport>()
520
+ const verifyAttestation = attestation
521
+ ? createAttestationGuard(attestation as Attestation.VerifierMap)
522
+ : undefined
468
523
 
469
524
  const handlers: Record<string, unknown> = {}
470
525
  const intentCount: Record<string, number> = {}
471
526
 
472
527
  for (const mi of methods) {
473
528
  intentCount[mi.intent] = (intentCount[mi.intent] ?? 0) + 1
529
+ if (mi.onPaymentSuccess) {
530
+ serverEvents.on('payment.success', (async (ctx: PaymentSuccessContext) => {
531
+ if (ctx.method.name === mi.name && ctx.method.intent === mi.intent) {
532
+ await mi.onPaymentSuccess({
533
+ input: ctx.input,
534
+ receipt: ctx.receipt,
535
+ request: ctx.request,
536
+ })
537
+ }
538
+ }) as never)
539
+ }
474
540
  }
475
541
  assertNoReservedMppxKeys(methods as readonly Method.AnyServer[])
476
542
 
477
543
  for (const mi of methods) {
478
544
  const fn = createMethodFn({
545
+ ...(verifyAttestation && { attest: verifyAttestation }),
479
546
  authorize: mi.authorize as never,
480
547
  defaults: mi.defaults,
481
548
  method: mi,
@@ -498,9 +565,32 @@ export function create<
498
565
  if (aliasKey) handlers[aliasKey] = fn
499
566
  }
500
567
 
501
- // Also set shorthand intent key when there's no collision
568
+ // Set shorthand intent key: single method gets direct handler,
569
+ // multiple methods get an implicit compose wrapper.
570
+ const processedIntents = new Set<string>()
502
571
  for (const mi of methods) {
503
- if (intentCount[mi.intent] === 1) handlers[mi.intent] = handlers[`${mi.name}/${mi.intent}`]
572
+ if (processedIntents.has(mi.intent)) continue
573
+ processedIntents.add(mi.intent)
574
+
575
+ if (intentCount[mi.intent] === 1) {
576
+ handlers[mi.intent] = handlers[`${mi.name}/${mi.intent}`]
577
+ } else {
578
+ const intentMethods = (methods as readonly Method.AnyServer[]).filter(
579
+ (m) => m.intent === mi.intent,
580
+ )
581
+ handlers[mi.intent] = (options: Record<string, unknown>) => {
582
+ const configured = intentMethods.map((m) => {
583
+ const key = `${m.name}/${m.intent}`
584
+ const handlerFn = handlers[key] as AnyMethodFn
585
+ return handlerFn(options)
586
+ })
587
+ return composeHandlers(
588
+ configured as ConfiguredHandler[],
589
+ undefined,
590
+ selectOffers as SelectOffers<readonly Method.AnyServer[]> | undefined,
591
+ )
592
+ }
593
+ }
504
594
  }
505
595
 
506
596
  // Build nested handlers: mppx.tempo.charge(...)
@@ -539,7 +629,7 @@ export function create<
539
629
  const methodCandidates = (methods as readonly Method.AnyServer[]).filter(
540
630
  (m) => m.name === credMethod && m.intent === credIntent,
541
631
  )
542
- const mi = Method.selectServerMethod(methodCandidates, credential.challenge)
632
+ const mi = methodCandidates[0]
543
633
  const eventMethod =
544
634
  mi ?? ({ intent: credIntent, name: credMethod } satisfies ServerMethodDescriptor)
545
635
 
@@ -806,7 +896,11 @@ export function create<
806
896
  throw new Error(`No handler for "${key}". Is this method in your methods array?`)
807
897
  return handlerFn(options)
808
898
  })
809
- return compose(...(configured as ConfiguredHandler[]))
899
+ return composeHandlers(
900
+ configured as ConfiguredHandler[],
901
+ undefined,
902
+ selectOffers as SelectOffers<readonly Method.AnyServer[]> | undefined,
903
+ )
810
904
  }
811
905
 
812
906
  function onChallengeCreated(
@@ -858,12 +952,18 @@ export declare namespace create {
858
952
  methods extends Methods = Methods,
859
953
  transport extends Transport.AnyTransport = Transport.Http,
860
954
  > = {
955
+ /** Request-attestation verifiers required before handling an HTTP payment request. */
956
+ attestation?: transport extends Transport.Http ? Attestation.VerifierMap | undefined : never
861
957
  /** Array of configured methods. @example [tempo()] */
862
958
  methods: methods
863
959
  /** Server realm (e.g., hostname). Resolution order: explicit value > env vars (`MPP_REALM`, `FLY_APP_NAME`, `VERCEL_URL`, etc.) > request URL hostname > `"MPP Payment"`. */
864
960
  realm?: string | undefined
865
961
  /** Secret key for HMAC-bound challenge IDs for stateless verification. Must be at least 32 bytes. Auto-detected from `MPP_SECRET_KEY` environment variable. */
866
962
  secretKey?: string | undefined
963
+ /** Selects a subset of composed HTTP offers before challenges are issued. Successfully matched credential dispatch bypasses this hook. */
964
+ selectOffers?: transport extends Transport.Http
965
+ ? SelectOffers<FlattenMethods<methods>> | undefined
966
+ : never
867
967
  /** Transport to use. @default Transport.http() */
868
968
  transport?: transport | undefined
869
969
  }
@@ -879,6 +979,7 @@ function createMethodFn<
879
979
  // biome-ignore lint/correctness/noUnusedVariables: _
880
980
  function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.ReturnType {
881
981
  const {
982
+ attest,
882
983
  authorize,
883
984
  defaults,
884
985
  events,
@@ -897,7 +998,7 @@ function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.R
897
998
  return (options) => {
898
999
  const { description, meta, scope, ...rest } = options
899
1000
  const staticMeta = Scope.merge({ meta, scope })
900
- const internal: ConfiguredHandler['_internal'] = {
1001
+ const internal = {
901
1002
  ...method,
902
1003
  ...defaults,
903
1004
  ...options,
@@ -907,9 +1008,21 @@ function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.R
907
1008
  html: method.html,
908
1009
  _canonicalRequest: PaymentRequest.fromMethod(method, { ...defaults, ...rest }),
909
1010
  _stableBinding: stableBinding as never,
910
- }
1011
+ } as unknown as ConfiguredHandler['_internal']
1012
+ Object.defineProperty(internal, '_method', { value: method })
911
1013
 
912
1014
  const handler = async (input: Transport.InputOf): Promise<MethodFn.Response> => {
1015
+ if (attest && input instanceof globalThis.Request) {
1016
+ const response = await attest(input)
1017
+ if (response)
1018
+ return {
1019
+ status: 200,
1020
+ withReceipt() {
1021
+ return response
1022
+ },
1023
+ } as MethodFn.Response
1024
+ }
1025
+
913
1026
  if (method.html && isServiceWorkerRequest(input))
914
1027
  return {
915
1028
  status: 402,
@@ -1454,6 +1567,7 @@ declare namespace createMethodFn {
1454
1567
  transport extends Transport.AnyTransport = Transport.Http,
1455
1568
  defaults extends Record<string, unknown> = Record<string, unknown>,
1456
1569
  > = {
1570
+ attest?: (request: globalThis.Request) => Promise<globalThis.Response | undefined>
1457
1571
  authorize?: Method.AuthorizeFn<method>
1458
1572
  defaults?: defaults
1459
1573
  method: method
@@ -1477,6 +1591,39 @@ declare namespace createMethodFn {
1477
1591
  > = MethodFn<method, transport, defaults>
1478
1592
  }
1479
1593
 
1594
+ function createAttestationGuard(verifiers: Attestation.VerifierMap) {
1595
+ const entries = Object.entries(verifiers)
1596
+ if (entries.length === 0)
1597
+ throw new TypeError('Mppx server attestation must configure at least one verifier.')
1598
+
1599
+ const cache = new WeakMap<globalThis.Request, Promise<globalThis.Response | undefined>>()
1600
+ return (request: globalThis.Request): Promise<globalThis.Response | undefined> => {
1601
+ const cached = cache.get(request)
1602
+ if (cached) return cached
1603
+
1604
+ const verification = AttestationServer.verify(request, verifiers).then((outcomes) => {
1605
+ for (const [name, outcome] of Object.entries(outcomes))
1606
+ if (outcome.status === 'invalid')
1607
+ return new globalThis.Response(
1608
+ `Request attestation "${name}" is invalid: ${outcome.reason}`,
1609
+ { status: 401 },
1610
+ )
1611
+
1612
+ for (const [name, outcome] of Object.entries(outcomes)) {
1613
+ if (outcome.status === 'verified') continue
1614
+ const reason =
1615
+ outcome.status === 'unverified'
1616
+ ? `Request attestation "${name}" could not be verified: ${outcome.reason}`
1617
+ : `Request attestation "${name}" is required.`
1618
+ return new globalThis.Response(reason, { status: 403 })
1619
+ }
1620
+ return undefined
1621
+ })
1622
+ cache.set(request, verification)
1623
+ return verification
1624
+ }
1625
+ }
1626
+
1480
1627
  type ServerEventDispatcher<
1481
1628
  methods extends readonly Method.Method[],
1482
1629
  transport extends Transport.AnyTransport,
@@ -2169,6 +2316,8 @@ declare namespace MethodFn {
2169
2316
  /** A configured handler — the return value of e.g. `mppx.charge({ ... })`. @internal */
2170
2317
  type ConfiguredHandler = ((input: Request) => Promise<MethodFn.Response<Transport.Http>>) & {
2171
2318
  _internal: {
2319
+ _method: Method.AnyServer
2320
+ description?: string | undefined
2172
2321
  name: string
2173
2322
  intent: string
2174
2323
  html: Html.Options | undefined
@@ -2179,6 +2328,16 @@ type ConfiguredHandler = ((input: Request) => Promise<MethodFn.Response<Transpor
2179
2328
  }
2180
2329
  }
2181
2330
 
2331
+ type ComposedHandler = ((input: Request) => Promise<MethodFn.Response<Transport.Http>>) & {
2332
+ _internal?: {
2333
+ offers: readonly ConfiguredHandler['_internal'][]
2334
+ }
2335
+ }
2336
+
2337
+ type HandlerDiscoveryMetadata =
2338
+ | ConfiguredHandler['_internal']
2339
+ | NonNullable<ComposedHandler['_internal']>
2340
+
2182
2341
  const paymentAuthChallengeHeader = Constants.Headers.wwwAuthenticate
2183
2342
 
2184
2343
  const challengeHeaderMerges = [
@@ -2252,9 +2411,7 @@ type ComposeEntry<methods extends readonly Method.AnyServer[]> =
2252
2411
  */
2253
2412
  type ComposeHtmlOptions = Html.Config
2254
2413
 
2255
- export function compose(
2256
- ...args: readonly unknown[]
2257
- ): (input: Request) => Promise<MethodFn.Response<Transport.Http>> {
2414
+ export function compose(...args: readonly unknown[]): ComposedHandler {
2258
2415
  // Extract optional html options from last argument
2259
2416
  const last = args[args.length - 1]
2260
2417
  const composeOptions: Html.Options | undefined =
@@ -2277,9 +2434,28 @@ export function compose(
2277
2434
  input: Request,
2278
2435
  ) => Promise<MethodFn.Response<Transport.Http>>)[]
2279
2436
 
2437
+ return composeHandlers(handlers, composeOptions)
2438
+ }
2439
+
2440
+ function composeHandlers(
2441
+ handlers: readonly ((input: Request) => Promise<MethodFn.Response<Transport.Http>>)[],
2442
+ composeOptions?: Html.Options,
2443
+ selectOffers?: SelectOffers<readonly Method.AnyServer[]>,
2444
+ ): (input: Request) => Promise<MethodFn.Response<Transport.Http>> {
2280
2445
  if (handlers.length === 0) throw new Error('compose() requires at least one handler')
2281
2446
 
2282
- return async (input: Request) => {
2447
+ const offerSelector = selectOffers
2448
+ const offerEntries = handlers.map((handler) => {
2449
+ const internal = (handler as ConfiguredHandler)._internal
2450
+ const offer =
2451
+ internal && !isComposedHandlerMetadata(internal) ? createServerOffer(internal) : undefined
2452
+ const method = internal && !isComposedHandlerMetadata(internal) ? internal._method : undefined
2453
+ return { handler, method, offer }
2454
+ })
2455
+ const hasOfferPolicy =
2456
+ offerSelector !== undefined || offerEntries.some(({ method }) => method?.canOffer !== undefined)
2457
+
2458
+ const composed: ComposedHandler = async (input: Request) => {
2283
2459
  // Serve service worker for html-enabled compose
2284
2460
  if (new URL(input.url).searchParams.has(Html.params.serviceWorker)) {
2285
2461
  const hasHtml = handlers.some((h) => (h as ConfiguredHandler)._internal?.html)
@@ -2314,45 +2490,53 @@ export function compose(
2314
2490
  const credReq = credential.challenge.request as Record<string, unknown>
2315
2491
 
2316
2492
  // Filter by name+intent, then narrow by comparing stable request fields
2317
- // from the echoed challenge against each handler's canonical request.
2493
+ // from the echoed challenge against each handler's configured offers.
2318
2494
  // Uses the schema-parsed canonical form (not raw options) so that
2319
2495
  // transformed fields (e.g. amount with decimals) match correctly.
2320
2496
  // Also checks inside methodDetails for fields moved there by transforms.
2321
- const candidates = handlers.filter((h) => {
2322
- try {
2323
- const internal = (h as ConfiguredHandler)._internal
2324
- if (!internal || internal.name !== credMethod || internal.intent !== credIntent)
2497
+ const candidates = handlers.filter((handler) =>
2498
+ getConfiguredOffers(handler).some((internal) => {
2499
+ try {
2500
+ if (internal.name !== credMethod || internal.intent !== credIntent) return false
2501
+ const mismatch = internal._stableBinding
2502
+ ? getRequestBindingMismatch(
2503
+ getStableBinding(internal._canonicalRequest, internal._stableBinding),
2504
+ getStableBinding(credReq, internal._stableBinding),
2505
+ )
2506
+ : getPinnedRequestBindingMismatch(internal._canonicalRequest, credReq)
2507
+ return !mismatch && opaqueValuesMatch(internal.meta, credential.challenge.meta)
2508
+ } catch {
2325
2509
  return false
2326
- const mismatch = internal._stableBinding
2327
- ? getRequestBindingMismatch(
2328
- getStableBinding(internal._canonicalRequest, internal._stableBinding),
2329
- getStableBinding(credReq, internal._stableBinding),
2330
- )
2331
- : getPinnedRequestBindingMismatch(internal._canonicalRequest, credReq)
2332
- return !mismatch && opaqueValuesMatch(internal.meta, credential.challenge.meta)
2333
- } catch {
2334
- return false
2335
- }
2336
- })
2510
+ }
2511
+ }),
2512
+ )
2337
2513
 
2338
2514
  const match =
2339
2515
  candidates[0] ??
2340
- handlers.find((h) => {
2341
- const meta = (h as ConfiguredHandler)._internal
2342
- return meta?.name === credMethod && meta?.intent === credIntent
2343
- })
2516
+ handlers.find((handler) =>
2517
+ getConfiguredOffers(handler).some(
2518
+ (internal) => internal.name === credMethod && internal.intent === credIntent,
2519
+ ),
2520
+ )
2344
2521
  if (match) return match(input)
2345
2522
  }
2346
2523
 
2347
- // Payment credential present but no matching handler dispatch to first
2348
- // handler which will reject with an appropriate error (invalid challenge, etc.).
2349
- return handlers[0]!(input)
2524
+ // Unrecognized or malformed credentials still use the currently eligible
2525
+ // offer set before the first handler issues its rejection challenge.
2350
2526
  }
2351
2527
 
2528
+ const selectedHandlers = hasOfferPolicy
2529
+ ? await selectOfferHandlers({
2530
+ entries: offerEntries,
2531
+ input,
2532
+ selectOffers: offerSelector,
2533
+ })
2534
+ : handlers
2535
+
2352
2536
  // No credential — evaluate handlers sequentially so authorize()/renewal hooks
2353
2537
  // can safely claim the request without racing each other.
2354
2538
  const results: MethodFn.Response<Transport.Http>[] = []
2355
- for (const handler of handlers) {
2539
+ for (const handler of selectedHandlers) {
2356
2540
  const result = await handler(input)
2357
2541
  if (result.status === 200) return result
2358
2542
  results.push(result)
@@ -2365,7 +2549,7 @@ export function compose(
2365
2549
  result: Extract<MethodFn.Response<Transport.Http>, { status: 402 }>
2366
2550
  }[] = []
2367
2551
 
2368
- for (let i = 0; i < handlers.length; i++) {
2552
+ for (let i = 0; i < selectedHandlers.length; i++) {
2369
2553
  const result = results[i]
2370
2554
  if (result?.status !== 402) continue
2371
2555
 
@@ -2374,7 +2558,7 @@ export function compose(
2374
2558
  if (!wwwAuth) continue
2375
2559
 
2376
2560
  entries.push({
2377
- handler: handlers[i] as ConfiguredHandler,
2561
+ handler: selectedHandlers[i] as ConfiguredHandler,
2378
2562
  challenge: Challenge.deserialize(wwwAuth),
2379
2563
  result,
2380
2564
  })
@@ -2501,6 +2685,118 @@ export function compose(
2501
2685
  challenge: new Response(body, { status: 402, headers: mergedHeaders }),
2502
2686
  }
2503
2687
  }
2688
+
2689
+ const offers = handlers.flatMap(getConfiguredOffers)
2690
+ if (offers.length > 0) composed._internal = { offers }
2691
+ return composed
2692
+ }
2693
+
2694
+ /** Returns the flattened configured offers accepted by a handler. */
2695
+ function getConfiguredOffers(
2696
+ handler: ConfiguredHandler | ComposedHandler,
2697
+ ): readonly ConfiguredHandler['_internal'][] {
2698
+ const internal = handler._internal
2699
+ if (!internal) return []
2700
+ if (isComposedHandlerMetadata(internal)) return internal.offers
2701
+ return [internal]
2702
+ }
2703
+
2704
+ function isComposedHandlerMetadata(
2705
+ internal: HandlerDiscoveryMetadata,
2706
+ ): internal is NonNullable<ComposedHandler['_internal']> {
2707
+ return 'offers' in internal && !('_canonicalRequest' in internal)
2708
+ }
2709
+
2710
+ function createServerOffer(internal: ConfiguredHandler['_internal']): ServerOffer {
2711
+ return Object.freeze({
2712
+ key: `${internal.name}/${internal.intent}`,
2713
+ method: Object.freeze({
2714
+ ...(internal._method.alias ? { alias: internal._method.alias } : {}),
2715
+ intent: internal.intent,
2716
+ name: internal.name,
2717
+ }),
2718
+ request: immutableSnapshot(internal._canonicalRequest),
2719
+ }) as ServerOffer
2720
+ }
2721
+
2722
+ /** Applies and validates the server-level offer selection policy. */
2723
+ async function selectOfferHandlers(parameters: {
2724
+ entries: readonly {
2725
+ handler: (input: Request) => Promise<MethodFn.Response<Transport.Http>>
2726
+ method: Method.AnyServer | undefined
2727
+ offer: ServerOffer | undefined
2728
+ }[]
2729
+ input: Request
2730
+ selectOffers: SelectOffers<readonly Method.AnyServer[]> | undefined
2731
+ }) {
2732
+ const { entries, input, selectOffers } = parameters
2733
+ const eligibleEntries: (typeof entries)[number][] = []
2734
+ for (const entry of entries) {
2735
+ const canOffer = entry.method?.canOffer
2736
+ if (canOffer) {
2737
+ const approved = await canOffer({
2738
+ input: cloneOfferInput(input),
2739
+ request: entry.offer!.request,
2740
+ })
2741
+ if (typeof approved !== 'boolean') throw new Error('canOffer() must return a boolean')
2742
+ if (!approved) continue
2743
+ }
2744
+ eligibleEntries.push(entry)
2745
+ }
2746
+
2747
+ if (eligibleEntries.length === 0)
2748
+ throw new Error('No payment offers are available for this request')
2749
+ if (!selectOffers) return eligibleEntries.map(({ handler }) => handler)
2750
+
2751
+ const offers = eligibleEntries.map(({ offer }) => {
2752
+ if (!offer) throw new Error('selectOffers() can only select configured payment offers')
2753
+ return offer
2754
+ })
2755
+ const selected = await selectOffers(
2756
+ Object.freeze(offers),
2757
+ Object.freeze({ request: cloneOfferInput(input) }),
2758
+ )
2759
+
2760
+ if (!Array.isArray(selected)) throw new Error('selectOffers() must return an array of offers')
2761
+ if (selected.length === 0) throw new Error('selectOffers() must select at least one offer')
2762
+
2763
+ let previousIndex = -1
2764
+ const selectedHandlers = selected.map((offer) => {
2765
+ const index = offers.indexOf(offer)
2766
+ if (index === -1) throw new Error('selectOffers() must return only offers from its input array')
2767
+ if (index <= previousIndex)
2768
+ throw new Error('selectOffers() must preserve offer order and must not return duplicates')
2769
+ previousIndex = index
2770
+ return eligibleEntries[index]!.handler
2771
+ })
2772
+
2773
+ return selectedHandlers
2774
+ }
2775
+
2776
+ /** Clones a request for offer hooks, falling back to a bodyless metadata copy. */
2777
+ function cloneOfferInput(input: Request): Request {
2778
+ try {
2779
+ return input.clone()
2780
+ } catch {
2781
+ return new globalThis.Request(input.url, {
2782
+ headers: new Headers(input.headers),
2783
+ method: input.method,
2784
+ signal: input.signal,
2785
+ })
2786
+ }
2787
+ }
2788
+
2789
+ /** Clones and recursively freezes protocol data exposed to offer hooks. */
2790
+ function immutableSnapshot<value>(value: value): DeepReadonly<value> {
2791
+ return deepFreeze(structuredClone(value))
2792
+ }
2793
+
2794
+ function deepFreeze<value>(value: value): DeepReadonly<value> {
2795
+ if (!value || typeof value !== 'object' || Object.isFrozen(value))
2796
+ return value as DeepReadonly<value>
2797
+
2798
+ for (const nested of Object.values(value)) deepFreeze(nested)
2799
+ return Object.freeze(value) as DeepReadonly<value>
2504
2800
  }
2505
2801
 
2506
2802
  type ChallengeHeaderMerge = {
@@ -4,4 +4,4 @@
4
4
  * Required for `shared_payment_granted_token` (SPTs are in private preview).
5
5
  * Bump this when upgrading to a newer Stripe API version.
6
6
  */
7
- export const stripePreviewVersion = '2026-02-25.preview'
7
+ export const stripePreviewVersion = '2026-07-29.preview'
@@ -14,6 +14,7 @@ export type StripeClient = {
14
14
  lastResponse?: { headers?: Record<string, string> }
15
15
  }>
16
16
  }
17
+ rawRequest?(method: string, path: string, params?: any, options?: any): Promise<any>
17
18
  }
18
19
 
19
20
  /**
@@ -1,5 +1,5 @@
1
1
  import { Mppx, stripe } from 'mppx/server'
2
- import { test } from 'vp/test'
2
+ import { expectTypeOf, test } from 'vp/test'
3
3
 
4
4
  import type { StripeClient } from '../internal/types.js'
5
5
  import type { charge as StripeCharge } from './Charge.js'
@@ -7,6 +7,21 @@ import type { charge as StripeCharge } from './Charge.js'
7
7
  const client = {} as StripeClient
8
8
  const secretKey = 'test-secret-key-test-secret-key-32'
9
9
 
10
+ test('accepts a typed constructor canOffer policy', () => {
11
+ stripe.charge({
12
+ client,
13
+ canOffer({ input, request }) {
14
+ expectTypeOf(input).toEqualTypeOf<Request>()
15
+ expectTypeOf(request.amount).toEqualTypeOf<string>()
16
+ expectTypeOf(request.currency).toEqualTypeOf<string>()
17
+ expectTypeOf(request.methodDetails.networkId).toEqualTypeOf<string>()
18
+ return request.amount !== '0'
19
+ },
20
+ networkId: 'internal',
21
+ paymentMethodTypes: ['card'],
22
+ })
23
+ })
24
+
10
25
  test('requires request fields not configured as Stripe defaults', () => {
11
26
  const server = Mppx.create({
12
27
  methods: [