mppx 0.8.15 → 0.8.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/Challenge.d.ts.map +1 -1
  3. package/dist/Challenge.js +40 -35
  4. package/dist/Challenge.js.map +1 -1
  5. package/dist/Constants.d.ts +17 -17
  6. package/dist/Constants.d.ts.map +1 -1
  7. package/dist/Credential.d.ts.map +1 -1
  8. package/dist/Errors.d.ts +3 -3
  9. package/dist/Errors.d.ts.map +1 -1
  10. package/dist/Errors.js.map +1 -1
  11. package/dist/Method.d.ts +35 -5
  12. package/dist/Method.d.ts.map +1 -1
  13. package/dist/Method.js +7 -22
  14. package/dist/Method.js.map +1 -1
  15. package/dist/Store.d.ts +19 -0
  16. package/dist/Store.d.ts.map +1 -1
  17. package/dist/Store.js +43 -0
  18. package/dist/Store.js.map +1 -1
  19. package/dist/attestation/Client.d.ts +17 -0
  20. package/dist/attestation/Client.d.ts.map +1 -0
  21. package/dist/attestation/Client.js +28 -0
  22. package/dist/attestation/Client.js.map +1 -0
  23. package/dist/attestation/Constants.d.ts +15 -0
  24. package/dist/attestation/Constants.d.ts.map +1 -0
  25. package/dist/attestation/Constants.js +15 -0
  26. package/dist/attestation/Constants.js.map +1 -0
  27. package/dist/attestation/Server.d.ts +4 -0
  28. package/dist/attestation/Server.d.ts.map +1 -0
  29. package/dist/attestation/Server.js +8 -0
  30. package/dist/attestation/Server.js.map +1 -0
  31. package/dist/attestation/Types.d.ts +76 -0
  32. package/dist/attestation/Types.d.ts.map +1 -0
  33. package/dist/attestation/Types.js +2 -0
  34. package/dist/attestation/Types.js.map +1 -0
  35. package/dist/attestation/index.d.ts +6 -0
  36. package/dist/attestation/index.d.ts.map +1 -0
  37. package/dist/attestation/index.js +6 -0
  38. package/dist/attestation/index.js.map +1 -0
  39. package/dist/attestation/internal/HttpMessageSignature.d.ts +73 -0
  40. package/dist/attestation/internal/HttpMessageSignature.d.ts.map +1 -0
  41. package/dist/attestation/internal/HttpMessageSignature.js +320 -0
  42. package/dist/attestation/internal/HttpMessageSignature.js.map +1 -0
  43. package/dist/attestation/internal/SigningContext.d.ts +4 -0
  44. package/dist/attestation/internal/SigningContext.d.ts.map +1 -0
  45. package/dist/attestation/internal/SigningContext.js +11 -0
  46. package/dist/attestation/internal/SigningContext.js.map +1 -0
  47. package/dist/cli/account.d.ts +14 -14
  48. package/dist/cli/account.d.ts.map +1 -1
  49. package/dist/cli/cli.d.ts +6 -6
  50. package/dist/cli/cli.d.ts.map +1 -1
  51. package/dist/cli/cli.js +1 -0
  52. package/dist/cli/cli.js.map +1 -1
  53. package/dist/cli/plugins/tempo.js.map +1 -1
  54. package/dist/cli/sessions/commands.d.ts +4 -4
  55. package/dist/cli/sessions/commands.d.ts.map +1 -1
  56. package/dist/cli/sessions/commands.js.map +1 -1
  57. package/dist/cli/sessions/store.d.ts.map +1 -1
  58. package/dist/cli/sessions/store.js.map +1 -1
  59. package/dist/cli/utils.d.ts +1 -1
  60. package/dist/cli/utils.d.ts.map +1 -1
  61. package/dist/cli/utils.js.map +1 -1
  62. package/dist/cli/validate/discovery.js.map +1 -1
  63. package/dist/cli/validate/index.d.ts +3 -3
  64. package/dist/cli/validate/index.d.ts.map +1 -1
  65. package/dist/client/Mppx.d.ts +3 -0
  66. package/dist/client/Mppx.d.ts.map +1 -1
  67. package/dist/client/Mppx.js +13 -5
  68. package/dist/client/Mppx.js.map +1 -1
  69. package/dist/client/Transport.d.ts +3 -5
  70. package/dist/client/Transport.d.ts.map +1 -1
  71. package/dist/client/Transport.js +1 -17
  72. package/dist/client/Transport.js.map +1 -1
  73. package/dist/client/internal/Fetch.d.ts.map +1 -1
  74. package/dist/client/internal/Fetch.js +2 -6
  75. package/dist/client/internal/Fetch.js.map +1 -1
  76. package/dist/client/node.js.map +1 -1
  77. package/dist/discovery/OpenApi.d.ts +3 -5
  78. package/dist/discovery/OpenApi.d.ts.map +1 -1
  79. package/dist/discovery/OpenApi.js +13 -34
  80. package/dist/discovery/OpenApi.js.map +1 -1
  81. package/dist/discovery/internal/Metadata.d.ts +20 -0
  82. package/dist/discovery/internal/Metadata.d.ts.map +1 -0
  83. package/dist/discovery/internal/Metadata.js +36 -0
  84. package/dist/discovery/internal/Metadata.js.map +1 -0
  85. package/dist/evm/Methods.d.ts +11 -11
  86. package/dist/evm/Methods.d.ts.map +1 -1
  87. package/dist/evm/Types.d.ts +17 -17
  88. package/dist/evm/Types.d.ts.map +1 -1
  89. package/dist/evm/client/Charge.d.ts +11 -11
  90. package/dist/evm/client/Methods.d.ts +11 -11
  91. package/dist/evm/server/Charge.d.ts +1 -1
  92. package/dist/evm/server/Charge.d.ts.map +1 -1
  93. package/dist/evm/server/Charge.js +2 -0
  94. package/dist/evm/server/Charge.js.map +1 -1
  95. package/dist/evm/server/Methods.d.ts +11 -11
  96. package/dist/internal/HeaderCodec.d.ts +2 -2
  97. package/dist/internal/HeaderCodec.d.ts.map +1 -1
  98. package/dist/internal/types.d.ts +6 -0
  99. package/dist/internal/types.d.ts.map +1 -1
  100. package/dist/middlewares/elysia.js.map +1 -1
  101. package/dist/middlewares/express.js.map +1 -1
  102. package/dist/middlewares/hono.js.map +1 -1
  103. package/dist/middlewares/nextjs.js.map +1 -1
  104. package/dist/proxy/Proxy.d.ts +2 -0
  105. package/dist/proxy/Proxy.d.ts.map +1 -1
  106. package/dist/proxy/Proxy.js +117 -9
  107. package/dist/proxy/Proxy.js.map +1 -1
  108. package/dist/proxy/Service.d.ts +36 -1
  109. package/dist/proxy/Service.d.ts.map +1 -1
  110. package/dist/proxy/Service.js +9 -8
  111. package/dist/proxy/Service.js.map +1 -1
  112. package/dist/proxy/services/anthropic.d.ts.map +1 -1
  113. package/dist/proxy/services/anthropic.js +1 -0
  114. package/dist/proxy/services/anthropic.js.map +1 -1
  115. package/dist/proxy/services/openai.d.ts.map +1 -1
  116. package/dist/proxy/services/openai.js +1 -0
  117. package/dist/proxy/services/openai.js.map +1 -1
  118. package/dist/proxy/services/stripe.d.ts.map +1 -1
  119. package/dist/proxy/services/stripe.js +1 -0
  120. package/dist/proxy/services/stripe.js.map +1 -1
  121. package/dist/server/Mppx.d.ts +75 -13
  122. package/dist/server/Mppx.d.ts.map +1 -1
  123. package/dist/server/Mppx.js +201 -23
  124. package/dist/server/Mppx.js.map +1 -1
  125. package/dist/server/internal/html/config.d.ts +9 -9
  126. package/dist/server/internal/html/config.d.ts.map +1 -1
  127. package/dist/server/internal/html/constants.d.ts +17 -17
  128. package/dist/server/internal/html/constants.d.ts.map +1 -1
  129. package/dist/stripe/Methods.d.ts +10 -10
  130. package/dist/stripe/Methods.d.ts.map +1 -1
  131. package/dist/stripe/client/Charge.d.ts +10 -10
  132. package/dist/stripe/client/Methods.d.ts +10 -10
  133. package/dist/stripe/internal/constants.d.ts +1 -1
  134. package/dist/stripe/internal/constants.js +1 -1
  135. package/dist/stripe/internal/types.d.ts +1 -0
  136. package/dist/stripe/internal/types.d.ts.map +1 -1
  137. package/dist/stripe/server/Charge.d.ts +11 -11
  138. package/dist/stripe/server/Charge.d.ts.map +1 -1
  139. package/dist/stripe/server/Charge.js +49 -1
  140. package/dist/stripe/server/Charge.js.map +1 -1
  141. package/dist/stripe/server/Methods.d.ts +137 -48
  142. package/dist/stripe/server/Methods.d.ts.map +1 -1
  143. package/dist/stripe/server/Methods.js +259 -4
  144. package/dist/stripe/server/Methods.js.map +1 -1
  145. package/dist/stripe/server/internal/deposit-address.d.ts +13 -0
  146. package/dist/stripe/server/internal/deposit-address.d.ts.map +1 -0
  147. package/dist/stripe/server/internal/deposit-address.js +28 -0
  148. package/dist/stripe/server/internal/deposit-address.js.map +1 -0
  149. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  150. package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
  151. package/dist/stripe/server/internal/html.gen.js +1 -1
  152. package/dist/stripe/server/internal/html.gen.js.map +1 -1
  153. package/dist/stripe/server/internal/record-payment.d.ts +15 -0
  154. package/dist/stripe/server/internal/record-payment.d.ts.map +1 -0
  155. package/dist/stripe/server/internal/record-payment.js +42 -0
  156. package/dist/stripe/server/internal/record-payment.js.map +1 -0
  157. package/dist/stripe/server/internal/request.d.ts +25 -0
  158. package/dist/stripe/server/internal/request.d.ts.map +1 -0
  159. package/dist/stripe/server/internal/request.js +29 -0
  160. package/dist/stripe/server/internal/request.js.map +1 -0
  161. package/dist/tap/Constants.d.ts +28 -0
  162. package/dist/tap/Constants.d.ts.map +1 -0
  163. package/dist/tap/Constants.js +32 -0
  164. package/dist/tap/Constants.js.map +1 -0
  165. package/dist/tap/Types.d.ts +11 -0
  166. package/dist/tap/Types.d.ts.map +1 -0
  167. package/dist/tap/Types.js +2 -0
  168. package/dist/tap/Types.js.map +1 -0
  169. package/dist/tap/client/index.d.ts +26 -0
  170. package/dist/tap/client/index.d.ts.map +1 -0
  171. package/dist/tap/client/index.js +33 -0
  172. package/dist/tap/client/index.js.map +1 -0
  173. package/dist/tap/index.d.ts +5 -0
  174. package/dist/tap/index.d.ts.map +1 -0
  175. package/dist/tap/index.js +5 -0
  176. package/dist/tap/index.js.map +1 -0
  177. package/dist/tap/server/index.d.ts +20 -0
  178. package/dist/tap/server/index.d.ts.map +1 -0
  179. package/dist/tap/server/index.js +36 -0
  180. package/dist/tap/server/index.js.map +1 -0
  181. package/dist/tempo/Attribution.d.ts +1 -1
  182. package/dist/tempo/Methods.d.ts +60 -60
  183. package/dist/tempo/Methods.d.ts.map +1 -1
  184. package/dist/tempo/Proof.d.ts +16 -16
  185. package/dist/tempo/client/Charge.d.ts +19 -19
  186. package/dist/tempo/client/Charge.d.ts.map +1 -1
  187. package/dist/tempo/client/Charge.js +1 -7
  188. package/dist/tempo/client/Charge.js.map +1 -1
  189. package/dist/tempo/client/Methods.d.ts +38 -38
  190. package/dist/tempo/client/Methods.js.map +1 -1
  191. package/dist/tempo/client/Subscription.d.ts +23 -23
  192. package/dist/tempo/client/Subscription.js.map +1 -1
  193. package/dist/tempo/internal/address.d.ts +2 -1
  194. package/dist/tempo/internal/address.d.ts.map +1 -1
  195. package/dist/tempo/internal/address.js +3 -1
  196. package/dist/tempo/internal/address.js.map +1 -1
  197. package/dist/tempo/internal/auto-swap.d.ts.map +1 -1
  198. package/dist/tempo/internal/auto-swap.js +4 -19
  199. package/dist/tempo/internal/auto-swap.js.map +1 -1
  200. package/dist/tempo/internal/defaults.d.ts +6 -6
  201. package/dist/tempo/internal/defaults.d.ts.map +1 -1
  202. package/dist/tempo/internal/fee-payer.d.ts +12 -12
  203. package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
  204. package/dist/tempo/internal/fee-payer.js.map +1 -1
  205. package/dist/tempo/internal/proof.d.ts +17 -17
  206. package/dist/tempo/internal/proof.d.ts.map +1 -1
  207. package/dist/tempo/legacy/client/Session.d.ts +20 -20
  208. package/dist/tempo/legacy/client/Session.js.map +1 -1
  209. package/dist/tempo/legacy/client/SessionManager.js.map +1 -1
  210. package/dist/tempo/legacy/index.d.ts +1 -3
  211. package/dist/tempo/legacy/index.d.ts.map +1 -1
  212. package/dist/tempo/legacy/index.js +1 -3
  213. package/dist/tempo/legacy/index.js.map +1 -1
  214. package/dist/tempo/legacy/session/escrow.abi.d.ts +374 -374
  215. package/dist/tempo/legacy/session/escrow.abi.d.ts.map +1 -1
  216. package/dist/tempo/server/Charge.d.ts +25 -11
  217. package/dist/tempo/server/Charge.d.ts.map +1 -1
  218. package/dist/tempo/server/Charge.js +44 -39
  219. package/dist/tempo/server/Charge.js.map +1 -1
  220. package/dist/tempo/server/Methods.d.ts +37 -52
  221. package/dist/tempo/server/Methods.d.ts.map +1 -1
  222. package/dist/tempo/server/Methods.js +0 -14
  223. package/dist/tempo/server/Methods.js.map +1 -1
  224. package/dist/tempo/server/Relay.d.ts +1 -1
  225. package/dist/tempo/server/Relay.d.ts.map +1 -1
  226. package/dist/tempo/server/Relay.js +2 -2
  227. package/dist/tempo/server/Relay.js.map +1 -1
  228. package/dist/tempo/server/Subscription.d.ts +25 -25
  229. package/dist/tempo/server/Subscription.d.ts.map +1 -1
  230. package/dist/tempo/server/Subscription.js +2 -0
  231. package/dist/tempo/server/Subscription.js.map +1 -1
  232. package/dist/tempo/server/index.d.ts +1 -1
  233. package/dist/tempo/server/index.d.ts.map +1 -1
  234. package/dist/tempo/server/index.js +1 -1
  235. package/dist/tempo/server/index.js.map +1 -1
  236. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  237. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
  238. package/dist/tempo/server/internal/html.gen.js +1 -1
  239. package/dist/tempo/server/internal/html.gen.js.map +1 -1
  240. package/dist/tempo/server/internal/transport.d.ts +2 -0
  241. package/dist/tempo/server/internal/transport.d.ts.map +1 -1
  242. package/dist/tempo/server/internal/transport.js +9 -2
  243. package/dist/tempo/server/internal/transport.js.map +1 -1
  244. package/dist/tempo/session/client/ChannelOps.js.map +1 -1
  245. package/dist/tempo/session/client/CredentialState.d.ts +1 -1
  246. package/dist/tempo/session/client/CredentialState.js.map +1 -1
  247. package/dist/tempo/session/client/ReceiptCoordinator.js.map +1 -1
  248. package/dist/tempo/session/client/Runtime.js.map +1 -1
  249. package/dist/tempo/session/client/Session.d.ts +19 -19
  250. package/dist/tempo/session/client/Session.js.map +1 -1
  251. package/dist/tempo/session/client/SessionManager.js.map +1 -1
  252. package/dist/tempo/session/client/Transports.d.ts.map +1 -1
  253. package/dist/tempo/session/precompile/Chain.d.ts +27 -0
  254. package/dist/tempo/session/precompile/Chain.d.ts.map +1 -1
  255. package/dist/tempo/session/precompile/Chain.js +68 -7
  256. package/dist/tempo/session/precompile/Chain.js.map +1 -1
  257. package/dist/tempo/session/precompile/Protocol.js.map +1 -1
  258. package/dist/tempo/session/precompile/Voucher.d.ts +4 -4
  259. package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -1
  260. package/dist/tempo/session/precompile/escrow.abi.d.ts +306 -306
  261. package/dist/tempo/session/precompile/escrow.abi.d.ts.map +1 -1
  262. package/dist/tempo/session/server/ChannelStore.d.ts +9 -0
  263. package/dist/tempo/session/server/ChannelStore.d.ts.map +1 -1
  264. package/dist/tempo/session/server/ChannelStore.js +16 -8
  265. package/dist/tempo/session/server/ChannelStore.js.map +1 -1
  266. package/dist/tempo/session/server/CredentialVerification.d.ts +30 -13
  267. package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -1
  268. package/dist/tempo/session/server/CredentialVerification.js +207 -13
  269. package/dist/tempo/session/server/CredentialVerification.js.map +1 -1
  270. package/dist/tempo/session/server/MeteredStream.d.ts +3 -0
  271. package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -1
  272. package/dist/tempo/session/server/MeteredStream.js +3 -1
  273. package/dist/tempo/session/server/MeteredStream.js.map +1 -1
  274. package/dist/tempo/session/server/Session.d.ts +32 -21
  275. package/dist/tempo/session/server/Session.d.ts.map +1 -1
  276. package/dist/tempo/session/server/Session.js +116 -53
  277. package/dist/tempo/session/server/Session.js.map +1 -1
  278. package/dist/tempo/session/server/Sse.d.ts +3 -1
  279. package/dist/tempo/session/server/Sse.d.ts.map +1 -1
  280. package/dist/tempo/session/server/Sse.js +2 -1
  281. package/dist/tempo/session/server/Sse.js.map +1 -1
  282. package/dist/tempo/session/server/Transports.d.ts +1 -1
  283. package/dist/tempo/session/server/Transports.d.ts.map +1 -1
  284. package/dist/tempo/session/server/Transports.js +1 -0
  285. package/dist/tempo/session/server/Transports.js.map +1 -1
  286. package/dist/tempo/session/server/Ws.d.ts +6 -1
  287. package/dist/tempo/session/server/Ws.d.ts.map +1 -1
  288. package/dist/tempo/session/server/Ws.js +1 -0
  289. package/dist/tempo/session/server/Ws.js.map +1 -1
  290. package/dist/tempo/subscription/KeyAuthorization.d.ts +39 -39
  291. package/dist/tempo/subscription/KeyAuthorization.d.ts.map +1 -1
  292. package/dist/viem/Account.d.ts.map +1 -1
  293. package/dist/viem/Account.js.map +1 -1
  294. package/dist/web-bot-auth/Constants.d.ts +20 -0
  295. package/dist/web-bot-auth/Constants.d.ts.map +1 -0
  296. package/dist/web-bot-auth/Constants.js +24 -0
  297. package/dist/web-bot-auth/Constants.js.map +1 -0
  298. package/dist/web-bot-auth/Types.d.ts +10 -0
  299. package/dist/web-bot-auth/Types.d.ts.map +1 -0
  300. package/dist/web-bot-auth/Types.js +2 -0
  301. package/dist/web-bot-auth/Types.js.map +1 -0
  302. package/dist/web-bot-auth/client/index.d.ts +29 -0
  303. package/dist/web-bot-auth/client/index.d.ts.map +1 -0
  304. package/dist/web-bot-auth/client/index.js +64 -0
  305. package/dist/web-bot-auth/client/index.js.map +1 -0
  306. package/dist/web-bot-auth/index.d.ts +7 -0
  307. package/dist/web-bot-auth/index.d.ts.map +1 -0
  308. package/dist/web-bot-auth/index.js +7 -0
  309. package/dist/web-bot-auth/index.js.map +1 -0
  310. package/dist/web-bot-auth/internal/JwkThumbprint.d.ts +5 -0
  311. package/dist/web-bot-auth/internal/JwkThumbprint.d.ts.map +1 -0
  312. package/dist/web-bot-auth/internal/JwkThumbprint.js +20 -0
  313. package/dist/web-bot-auth/internal/JwkThumbprint.js.map +1 -0
  314. package/dist/web-bot-auth/internal/SignatureAgent.d.ts +3 -0
  315. package/dist/web-bot-auth/internal/SignatureAgent.d.ts.map +1 -0
  316. package/dist/web-bot-auth/internal/SignatureAgent.js +18 -0
  317. package/dist/web-bot-auth/internal/SignatureAgent.js.map +1 -0
  318. package/dist/web-bot-auth/server/index.d.ts +35 -0
  319. package/dist/web-bot-auth/server/index.d.ts.map +1 -0
  320. package/dist/web-bot-auth/server/index.js +88 -0
  321. package/dist/web-bot-auth/server/index.js.map +1 -0
  322. package/dist/x402/Assets.d.ts.map +1 -1
  323. package/dist/x402/Types.d.ts +6 -6
  324. package/dist/x402/Types.d.ts.map +1 -1
  325. package/dist/x402/server/EvmCharge.d.ts.map +1 -1
  326. package/dist/x402/server/EvmCharge.js +32 -20
  327. package/dist/x402/server/EvmCharge.js.map +1 -1
  328. package/package.json +19 -3
  329. package/src/Challenge.fuzz.test.ts +80 -47
  330. package/src/Challenge.test.ts +65 -0
  331. package/src/Challenge.ts +39 -35
  332. package/src/Method.ts +82 -68
  333. package/src/Store.test-d.ts +16 -0
  334. package/src/Store.test.ts +50 -0
  335. package/src/Store.ts +68 -0
  336. package/src/attestation/Attestation.integration.test.ts +208 -0
  337. package/src/attestation/Client.test.ts +83 -0
  338. package/src/attestation/Client.ts +34 -0
  339. package/src/attestation/Constants.ts +15 -0
  340. package/src/attestation/Server.test.ts +60 -0
  341. package/src/attestation/Server.ts +12 -0
  342. package/src/attestation/Types.test-d.ts +54 -0
  343. package/src/attestation/Types.ts +77 -0
  344. package/src/attestation/index.ts +5 -0
  345. package/src/attestation/internal/HttpMessageSignature.test.ts +101 -0
  346. package/src/attestation/internal/HttpMessageSignature.ts +429 -0
  347. package/src/attestation/internal/SigningContext.ts +13 -0
  348. package/src/bin.js +2 -0
  349. package/src/cli/cli.ts +1 -0
  350. package/src/client/Mppx.test-d.ts +17 -0
  351. package/src/client/Mppx.test.ts +14 -14
  352. package/src/client/Mppx.ts +25 -4
  353. package/src/client/Transport.test.ts +12 -45
  354. package/src/client/Transport.ts +3 -24
  355. package/src/client/internal/Fetch.ts +2 -6
  356. package/src/discovery/OpenApi.test.ts +170 -0
  357. package/src/discovery/OpenApi.ts +16 -48
  358. package/src/discovery/internal/Metadata.ts +51 -0
  359. package/src/evm/server/Charge.ts +6 -1
  360. package/src/internal/AcceptPayment.test.ts +12 -8
  361. package/src/internal/types.ts +9 -0
  362. package/src/mcp/client/McpClient.integration.test.ts +19 -32
  363. package/src/middlewares/elysia.test.ts +2 -12
  364. package/src/middlewares/express.test.ts +2 -12
  365. package/src/middlewares/hono.test.ts +6 -9
  366. package/src/middlewares/nextjs.test.ts +2 -12
  367. package/src/proxy/Proxy.test.ts +26 -19
  368. package/src/proxy/Proxy.ts +147 -9
  369. package/src/proxy/Proxy.upstreamError.test.ts +364 -0
  370. package/src/proxy/Service.test.ts +73 -3
  371. package/src/proxy/Service.ts +50 -23
  372. package/src/proxy/services/anthropic.ts +1 -0
  373. package/src/proxy/services/hooks.test-d.ts +14 -0
  374. package/src/proxy/services/hooks.test.ts +44 -0
  375. package/src/proxy/services/openai.ts +1 -0
  376. package/src/proxy/services/stripe.ts +1 -0
  377. package/src/server/Methods.test-d.ts +88 -0
  378. package/src/server/Methods.test.ts +69 -0
  379. package/src/server/Mppx.test-d.ts +141 -7
  380. package/src/server/Mppx.test.ts +1017 -119
  381. package/src/server/Mppx.ts +350 -54
  382. package/src/stripe/internal/constants.ts +1 -1
  383. package/src/stripe/internal/types.ts +1 -0
  384. package/src/stripe/server/Charge.test-d.ts +16 -1
  385. package/src/stripe/server/Charge.test.ts +79 -0
  386. package/src/stripe/server/Charge.ts +54 -1
  387. package/src/stripe/server/Methods.test-d.ts +157 -0
  388. package/src/stripe/server/Methods.test.ts +403 -0
  389. package/src/stripe/server/Methods.ts +423 -6
  390. package/src/stripe/server/internal/deposit-address.ts +57 -0
  391. package/src/stripe/server/internal/html/node_modules/.bin/mppx +3 -3
  392. package/src/stripe/server/internal/html/package.json +1 -1
  393. package/src/stripe/server/internal/html.gen.ts +1 -1
  394. package/src/stripe/server/internal/record-payment.ts +66 -0
  395. package/src/stripe/server/internal/request.ts +49 -0
  396. package/src/tap/Constants.ts +32 -0
  397. package/src/tap/Tap.test.ts +125 -0
  398. package/src/tap/Types.ts +11 -0
  399. package/src/tap/client/index.ts +54 -0
  400. package/src/tap/index.ts +4 -0
  401. package/src/tap/server/index.ts +48 -0
  402. package/src/tempo/PublicExports.test-d.ts +3 -3
  403. package/src/tempo/client/Charge.ts +1 -19
  404. package/src/tempo/internal/address.test.ts +42 -0
  405. package/src/tempo/internal/address.ts +3 -3
  406. package/src/tempo/internal/auto-swap.test.ts +4 -4
  407. package/src/tempo/internal/auto-swap.ts +7 -50
  408. package/src/tempo/legacy/README.md +5 -5
  409. package/src/tempo/legacy/index.ts +1 -3
  410. package/src/tempo/server/AtomicStore.test-d.ts +13 -0
  411. package/src/tempo/server/Charge.test.ts +19 -84
  412. package/src/tempo/server/Charge.ts +83 -55
  413. package/src/tempo/server/Methods.ts +0 -20
  414. package/src/tempo/server/Relay.test.ts +5 -5
  415. package/src/tempo/server/Relay.ts +2 -2
  416. package/src/tempo/server/Subscription.ts +4 -1
  417. package/src/tempo/server/index.ts +1 -1
  418. package/src/tempo/server/internal/html/node_modules/.bin/mppx +3 -3
  419. package/src/tempo/server/internal/html/package.json +1 -1
  420. package/src/tempo/server/internal/html.gen.ts +1 -1
  421. package/src/tempo/server/internal/transport.test.ts +22 -3
  422. package/src/tempo/server/internal/transport.ts +15 -3
  423. package/src/tempo/session/README.md +112 -0
  424. package/src/tempo/session/precompile/Chain.ts +131 -11
  425. package/src/tempo/session/server/ChannelStore.ts +33 -12
  426. package/src/tempo/session/server/CredentialVerification.test.ts +4 -4
  427. package/src/tempo/session/server/CredentialVerification.ts +340 -28
  428. package/src/tempo/session/server/MeteredStream.ts +5 -1
  429. package/src/tempo/session/server/Session.test.ts +428 -43
  430. package/src/tempo/session/server/Session.ts +159 -60
  431. package/src/tempo/session/server/Sse.test.ts +77 -0
  432. package/src/tempo/session/server/Sse.ts +8 -1
  433. package/src/tempo/session/server/Transports.ts +2 -1
  434. package/src/tempo/session/server/Ws.test.ts +171 -0
  435. package/src/tempo/session/server/Ws.ts +7 -1
  436. package/src/web-bot-auth/Constants.ts +24 -0
  437. package/src/web-bot-auth/Types.ts +9 -0
  438. package/src/web-bot-auth/WebBotAuth.test.ts +182 -0
  439. package/src/web-bot-auth/client/index.ts +84 -0
  440. package/src/web-bot-auth/index.ts +6 -0
  441. package/src/web-bot-auth/internal/JwkThumbprint.ts +21 -0
  442. package/src/web-bot-auth/internal/SignatureAgent.ts +18 -0
  443. package/src/web-bot-auth/server/index.ts +113 -0
  444. package/src/x402/Exact.e2e.test.ts +121 -81
  445. package/src/x402/Exact.localnet.test.ts +1 -1
  446. package/src/x402/server/EvmCharge.ts +35 -19
  447. package/dist/tempo/legacy/server/Session.d.ts +0 -208
  448. package/dist/tempo/legacy/server/Session.d.ts.map +0 -1
  449. package/dist/tempo/legacy/server/Session.js +0 -698
  450. package/dist/tempo/legacy/server/Session.js.map +0 -1
  451. package/dist/tempo/legacy/server/index.d.ts +0 -5
  452. package/dist/tempo/legacy/server/index.d.ts.map +0 -1
  453. package/dist/tempo/legacy/server/index.js +0 -5
  454. package/dist/tempo/legacy/server/index.js.map +0 -1
  455. package/dist/tempo/legacy/session/ChannelStore.d.ts +0 -22
  456. package/dist/tempo/legacy/session/ChannelStore.d.ts.map +0 -1
  457. package/dist/tempo/legacy/session/ChannelStore.js +0 -6
  458. package/dist/tempo/legacy/session/ChannelStore.js.map +0 -1
  459. package/src/tempo/legacy/server/Defaults.test-d.ts +0 -9
  460. package/src/tempo/legacy/server/Session.test.ts +0 -6370
  461. package/src/tempo/legacy/server/Session.ts +0 -1024
  462. package/src/tempo/legacy/server/index.ts +0 -4
  463. package/src/tempo/legacy/session/ChannelStore.test.ts +0 -58
  464. package/src/tempo/legacy/session/ChannelStore.ts +0 -39
package/src/Challenge.ts CHANGED
@@ -366,35 +366,12 @@ export function deserialize<const methods extends readonly Method.Method[] | und
366
366
  /** @internal Extracts the `Payment` scheme from a WWW-Authenticate value that may contain multiple schemes. */
367
367
  function extractPaymentAuthParams(header: string): string | null {
368
368
  const token = Constants.Schemes.payment
369
- let inQuotes = false
370
- let escaped = false
371
-
372
- for (let i = 0; i < header.length; i++) {
373
- const char = header[i]
374
-
375
- if (inQuotes) {
376
- if (escaped) escaped = false
377
- else if (char === '\\') escaped = true
378
- else if (char === '"') inQuotes = false
379
- continue
380
- }
381
-
382
- if (char === '"') {
383
- inQuotes = true
384
- continue
385
- }
386
-
387
- if (!startsWithSchemeToken(header, i, token)) continue
388
-
389
- const prefix = header.slice(0, i)
390
- if (prefix.trim() && !prefix.trimEnd().endsWith(',')) continue
369
+ const [start] = findSchemeStarts(header, token)
370
+ if (start === undefined) return null
391
371
 
392
- let paramsStart = i + token.length
393
- while (paramsStart < header.length && /\s/.test(header[paramsStart] ?? '')) paramsStart++
394
- return header.slice(paramsStart)
395
- }
396
-
397
- return null
372
+ let paramsStart = start + token.length
373
+ while (paramsStart < header.length && /\s/.test(header[paramsStart] ?? '')) paramsStart++
374
+ return header.slice(paramsStart)
398
375
  }
399
376
 
400
377
  /** @internal Parses auth-params with support for escaped quoted-string values. */
@@ -481,11 +458,43 @@ function readQuotedAuthParamValue(
481
458
 
482
459
  /** @internal */
483
460
  function startsWithSchemeToken(value: string, index: number, token: string): boolean {
484
- if (!value.slice(index).toLowerCase().startsWith(token.toLowerCase())) return false
461
+ if (value.slice(index, index + token.length).toLowerCase() !== token.toLowerCase()) return false
485
462
  const next = value[index + token.length]
486
463
  return Boolean(next && /\s/.test(next))
487
464
  }
488
465
 
466
+ /** @internal Finds auth scheme boundaries while ignoring quoted-string contents. */
467
+ function findSchemeStarts(value: string, token: string): number[] {
468
+ const starts: number[] = []
469
+ let inQuotes = false
470
+ let escaped = false
471
+
472
+ for (let i = 0; i < value.length; i++) {
473
+ const char = value[i]
474
+
475
+ if (inQuotes) {
476
+ if (escaped) escaped = false
477
+ else if (char === '\\') escaped = true
478
+ else if (char === '"') inQuotes = false
479
+ continue
480
+ }
481
+
482
+ if (char === '"') {
483
+ inQuotes = true
484
+ continue
485
+ }
486
+
487
+ if (!startsWithSchemeToken(value, i, token)) continue
488
+
489
+ let boundary = i - 1
490
+ while (boundary >= 0 && /\s/.test(value[boundary] ?? '')) boundary--
491
+ if (boundary >= 0 && value[boundary] !== ',') continue
492
+ starts.push(i)
493
+ }
494
+
495
+ return starts
496
+ }
497
+
489
498
  /**
490
499
  * Extracts the challenge from a Headers object.
491
500
  *
@@ -594,12 +603,7 @@ export function fromHeadersList<
594
603
  export function deserializeList<
595
604
  const methods extends readonly Method.Method[] | undefined = undefined,
596
605
  >(value: string, options?: from.Options<methods>): from.ReturnType<from.Parameters, methods>[] {
597
- // Find the start index of each `Payment ` scheme prefix.
598
- const starts: number[] = []
599
- const schemePattern = new RegExp(`${Constants.Schemes.payment}\\s+`, 'gi')
600
- for (const match of value.matchAll(schemePattern)) {
601
- starts.push(match.index!)
602
- }
606
+ const starts = findSchemeStarts(value, Constants.Schemes.payment)
603
607
  if (starts.length === 0) throw new Error('No Payment schemes found.')
604
608
 
605
609
  // Slice each scheme from its `Payment ` prefix up to the next scheme boundary,
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
  )