mppx 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (446) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -0
  3. package/dist/BodyDigest.d.ts +42 -0
  4. package/dist/BodyDigest.d.ts.map +1 -0
  5. package/dist/BodyDigest.js +40 -0
  6. package/dist/BodyDigest.js.map +1 -0
  7. package/dist/Challenge.d.ts +271 -0
  8. package/dist/Challenge.d.ts.map +1 -0
  9. package/dist/Challenge.js +291 -0
  10. package/dist/Challenge.js.map +1 -0
  11. package/dist/Credential.d.ts +91 -0
  12. package/dist/Credential.d.ts.map +1 -0
  13. package/dist/Credential.js +122 -0
  14. package/dist/Credential.js.map +1 -0
  15. package/dist/Errors.d.ts +243 -0
  16. package/dist/Errors.d.ts.map +1 -0
  17. package/dist/Errors.js +201 -0
  18. package/dist/Errors.js.map +1 -0
  19. package/dist/Expires.d.ts +15 -0
  20. package/dist/Expires.d.ts.map +1 -0
  21. package/dist/Expires.js +29 -0
  22. package/dist/Expires.js.map +1 -0
  23. package/dist/Intent.d.ts +101 -0
  24. package/dist/Intent.d.ts.map +1 -0
  25. package/dist/Intent.js +83 -0
  26. package/dist/Intent.js.map +1 -0
  27. package/dist/Mcp.d.ts +74 -0
  28. package/dist/Mcp.d.ts.map +1 -0
  29. package/dist/Mcp.js +9 -0
  30. package/dist/Mcp.js.map +1 -0
  31. package/dist/MethodIntent.d.ts +225 -0
  32. package/dist/MethodIntent.d.ts.map +1 -0
  33. package/dist/MethodIntent.js +156 -0
  34. package/dist/MethodIntent.js.map +1 -0
  35. package/dist/PaymentRequest.d.ts +88 -0
  36. package/dist/PaymentRequest.d.ts.map +1 -0
  37. package/dist/PaymentRequest.js +81 -0
  38. package/dist/PaymentRequest.js.map +1 -0
  39. package/dist/Receipt.d.ts +110 -0
  40. package/dist/Receipt.d.ts.map +1 -0
  41. package/dist/Receipt.js +105 -0
  42. package/dist/Receipt.js.map +1 -0
  43. package/dist/Store.d.ts +28 -0
  44. package/dist/Store.d.ts.map +1 -0
  45. package/dist/Store.js +61 -0
  46. package/dist/Store.js.map +1 -0
  47. package/dist/cli.d.ts +3 -0
  48. package/dist/cli.d.ts.map +1 -0
  49. package/dist/cli.js +1219 -0
  50. package/dist/cli.js.map +1 -0
  51. package/dist/client/Methods.d.ts +4 -0
  52. package/dist/client/Methods.d.ts.map +1 -0
  53. package/dist/client/Methods.js +4 -0
  54. package/dist/client/Methods.js.map +1 -0
  55. package/dist/client/Mppx.d.ts +84 -0
  56. package/dist/client/Mppx.d.ts.map +1 -0
  57. package/dist/client/Mppx.js +64 -0
  58. package/dist/client/Mppx.js.map +1 -0
  59. package/dist/client/Transport.d.ts +56 -0
  60. package/dist/client/Transport.d.ts.map +1 -0
  61. package/dist/client/Transport.js +81 -0
  62. package/dist/client/Transport.js.map +1 -0
  63. package/dist/client/index.d.ts +5 -0
  64. package/dist/client/index.d.ts.map +1 -0
  65. package/dist/client/index.js +5 -0
  66. package/dist/client/index.js.map +1 -0
  67. package/dist/client/internal/Fetch.d.ts +85 -0
  68. package/dist/client/internal/Fetch.d.ts.map +1 -0
  69. package/dist/client/internal/Fetch.js +95 -0
  70. package/dist/client/internal/Fetch.js.map +1 -0
  71. package/dist/index.d.ts +13 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +13 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/internal/types.d.ts +302 -0
  76. package/dist/internal/types.d.ts.map +1 -0
  77. package/dist/internal/types.js +2 -0
  78. package/dist/internal/types.js.map +1 -0
  79. package/dist/mcp-sdk/client/McpClient.d.ts +78 -0
  80. package/dist/mcp-sdk/client/McpClient.d.ts.map +1 -0
  81. package/dist/mcp-sdk/client/McpClient.js +98 -0
  82. package/dist/mcp-sdk/client/McpClient.js.map +1 -0
  83. package/dist/mcp-sdk/client/index.d.ts +3 -0
  84. package/dist/mcp-sdk/client/index.d.ts.map +1 -0
  85. package/dist/mcp-sdk/client/index.js +3 -0
  86. package/dist/mcp-sdk/client/index.js.map +1 -0
  87. package/dist/mcp-sdk/server/Transport.d.ts +43 -0
  88. package/dist/mcp-sdk/server/Transport.d.ts.map +1 -0
  89. package/dist/mcp-sdk/server/Transport.js +71 -0
  90. package/dist/mcp-sdk/server/Transport.js.map +1 -0
  91. package/dist/mcp-sdk/server/index.d.ts +2 -0
  92. package/dist/mcp-sdk/server/index.d.ts.map +1 -0
  93. package/dist/mcp-sdk/server/index.js +2 -0
  94. package/dist/mcp-sdk/server/index.js.map +1 -0
  95. package/dist/middlewares/elysia.d.ts +51 -0
  96. package/dist/middlewares/elysia.d.ts.map +1 -0
  97. package/dist/middlewares/elysia.js +59 -0
  98. package/dist/middlewares/elysia.js.map +1 -0
  99. package/dist/middlewares/express.d.ts +46 -0
  100. package/dist/middlewares/express.d.ts.map +1 -0
  101. package/dist/middlewares/express.js +69 -0
  102. package/dist/middlewares/express.js.map +1 -0
  103. package/dist/middlewares/hono.d.ts +46 -0
  104. package/dist/middlewares/hono.d.ts.map +1 -0
  105. package/dist/middlewares/hono.js +57 -0
  106. package/dist/middlewares/hono.js.map +1 -0
  107. package/dist/middlewares/internal/mppx.d.ts +16 -0
  108. package/dist/middlewares/internal/mppx.d.ts.map +1 -0
  109. package/dist/middlewares/internal/mppx.js +16 -0
  110. package/dist/middlewares/internal/mppx.js.map +1 -0
  111. package/dist/middlewares/nextjs.d.ts +45 -0
  112. package/dist/middlewares/nextjs.d.ts.map +1 -0
  113. package/dist/middlewares/nextjs.js +57 -0
  114. package/dist/middlewares/nextjs.js.map +1 -0
  115. package/dist/proxy/Proxy.d.ts +47 -0
  116. package/dist/proxy/Proxy.d.ts.map +1 -0
  117. package/dist/proxy/Proxy.js +126 -0
  118. package/dist/proxy/Proxy.js.map +1 -0
  119. package/dist/proxy/Service.d.ts +100 -0
  120. package/dist/proxy/Service.d.ts.map +1 -0
  121. package/dist/proxy/Service.js +147 -0
  122. package/dist/proxy/Service.js.map +1 -0
  123. package/dist/proxy/index.d.ts +7 -0
  124. package/dist/proxy/index.d.ts.map +1 -0
  125. package/dist/proxy/index.js +7 -0
  126. package/dist/proxy/index.js.map +1 -0
  127. package/dist/proxy/internal/Headers.d.ts +3 -0
  128. package/dist/proxy/internal/Headers.d.ts.map +1 -0
  129. package/dist/proxy/internal/Headers.js +41 -0
  130. package/dist/proxy/internal/Headers.js.map +1 -0
  131. package/dist/proxy/internal/Route.d.ts +14 -0
  132. package/dist/proxy/internal/Route.d.ts.map +1 -0
  133. package/dist/proxy/internal/Route.js +47 -0
  134. package/dist/proxy/internal/Route.js.map +1 -0
  135. package/dist/proxy/services/anthropic.d.ts +29 -0
  136. package/dist/proxy/services/anthropic.d.ts.map +1 -0
  137. package/dist/proxy/services/anthropic.js +30 -0
  138. package/dist/proxy/services/anthropic.js.map +1 -0
  139. package/dist/proxy/services/openai.d.ts +29 -0
  140. package/dist/proxy/services/openai.d.ts.map +1 -0
  141. package/dist/proxy/services/openai.js +30 -0
  142. package/dist/proxy/services/openai.js.map +1 -0
  143. package/dist/proxy/services/stripe.d.ts +29 -0
  144. package/dist/proxy/services/stripe.d.ts.map +1 -0
  145. package/dist/proxy/services/stripe.js +30 -0
  146. package/dist/proxy/services/stripe.js.map +1 -0
  147. package/dist/server/Methods.d.ts +3 -0
  148. package/dist/server/Methods.d.ts.map +1 -0
  149. package/dist/server/Methods.js +3 -0
  150. package/dist/server/Methods.js.map +1 -0
  151. package/dist/server/Mppx.d.ts +116 -0
  152. package/dist/server/Mppx.d.ts.map +1 -0
  153. package/dist/server/Mppx.js +207 -0
  154. package/dist/server/Mppx.js.map +1 -0
  155. package/dist/server/NodeListener.d.ts +3 -0
  156. package/dist/server/NodeListener.d.ts.map +1 -0
  157. package/dist/server/NodeListener.js +3 -0
  158. package/dist/server/NodeListener.js.map +1 -0
  159. package/dist/server/Request.d.ts +24 -0
  160. package/dist/server/Request.d.ts.map +1 -0
  161. package/dist/server/Request.js +26 -0
  162. package/dist/server/Request.js.map +1 -0
  163. package/dist/server/Response.d.ts +10 -0
  164. package/dist/server/Response.d.ts.map +1 -0
  165. package/dist/server/Response.js +15 -0
  166. package/dist/server/Response.js.map +1 -0
  167. package/dist/server/Transport.d.ts +93 -0
  168. package/dist/server/Transport.d.ts.map +1 -0
  169. package/dist/server/Transport.js +132 -0
  170. package/dist/server/Transport.js.map +1 -0
  171. package/dist/server/index.d.ts +9 -0
  172. package/dist/server/index.d.ts.map +1 -0
  173. package/dist/server/index.js +9 -0
  174. package/dist/server/index.js.map +1 -0
  175. package/dist/stripe/Intents.d.ts +54 -0
  176. package/dist/stripe/Intents.d.ts.map +1 -0
  177. package/dist/stripe/Intents.js +27 -0
  178. package/dist/stripe/Intents.js.map +1 -0
  179. package/dist/stripe/client/Charge.d.ts +114 -0
  180. package/dist/stripe/client/Charge.d.ts.map +1 -0
  181. package/dist/stripe/client/Charge.js +77 -0
  182. package/dist/stripe/client/Charge.js.map +1 -0
  183. package/dist/stripe/client/MethodIntents.d.ts +80 -0
  184. package/dist/stripe/client/MethodIntents.d.ts.map +1 -0
  185. package/dist/stripe/client/MethodIntents.js +34 -0
  186. package/dist/stripe/client/MethodIntents.js.map +1 -0
  187. package/dist/stripe/client/index.d.ts +3 -0
  188. package/dist/stripe/client/index.d.ts.map +1 -0
  189. package/dist/stripe/client/index.js +3 -0
  190. package/dist/stripe/client/index.js.map +1 -0
  191. package/dist/stripe/index.d.ts +2 -0
  192. package/dist/stripe/index.d.ts.map +1 -0
  193. package/dist/stripe/index.js +2 -0
  194. package/dist/stripe/index.js.map +1 -0
  195. package/dist/stripe/server/Charge.d.ts +74 -0
  196. package/dist/stripe/server/Charge.d.ts.map +1 -0
  197. package/dist/stripe/server/Charge.js +79 -0
  198. package/dist/stripe/server/Charge.js.map +1 -0
  199. package/dist/stripe/server/MethodIntents.d.ts +65 -0
  200. package/dist/stripe/server/MethodIntents.d.ts.map +1 -0
  201. package/dist/stripe/server/MethodIntents.js +21 -0
  202. package/dist/stripe/server/MethodIntents.js.map +1 -0
  203. package/dist/stripe/server/index.d.ts +3 -0
  204. package/dist/stripe/server/index.d.ts.map +1 -0
  205. package/dist/stripe/server/index.js +3 -0
  206. package/dist/stripe/server/index.js.map +1 -0
  207. package/dist/tempo/Attribution.d.ts +101 -0
  208. package/dist/tempo/Attribution.d.ts.map +1 -0
  209. package/dist/tempo/Attribution.js +124 -0
  210. package/dist/tempo/Attribution.js.map +1 -0
  211. package/dist/tempo/Intents.d.ts +132 -0
  212. package/dist/tempo/Intents.d.ts.map +1 -0
  213. package/dist/tempo/Intents.js +81 -0
  214. package/dist/tempo/Intents.js.map +1 -0
  215. package/dist/tempo/client/ChannelOps.d.ts +54 -0
  216. package/dist/tempo/client/ChannelOps.d.ts.map +1 -0
  217. package/dist/tempo/client/ChannelOps.js +138 -0
  218. package/dist/tempo/client/ChannelOps.js.map +1 -0
  219. package/dist/tempo/client/Charge.d.ts +76 -0
  220. package/dist/tempo/client/Charge.d.ts.map +1 -0
  221. package/dist/tempo/client/Charge.js +69 -0
  222. package/dist/tempo/client/Charge.js.map +1 -0
  223. package/dist/tempo/client/MethodIntents.d.ts +157 -0
  224. package/dist/tempo/client/MethodIntents.d.ts.map +1 -0
  225. package/dist/tempo/client/MethodIntents.js +25 -0
  226. package/dist/tempo/client/MethodIntents.js.map +1 -0
  227. package/dist/tempo/client/Session.d.ts +159 -0
  228. package/dist/tempo/client/Session.d.ts.map +1 -0
  229. package/dist/tempo/client/Session.js +263 -0
  230. package/dist/tempo/client/Session.js.map +1 -0
  231. package/dist/tempo/client/SessionManager.d.ts +62 -0
  232. package/dist/tempo/client/SessionManager.d.ts.map +1 -0
  233. package/dist/tempo/client/SessionManager.js +196 -0
  234. package/dist/tempo/client/SessionManager.js.map +1 -0
  235. package/dist/tempo/client/index.d.ts +6 -0
  236. package/dist/tempo/client/index.d.ts.map +1 -0
  237. package/dist/tempo/client/index.js +5 -0
  238. package/dist/tempo/client/index.js.map +1 -0
  239. package/dist/tempo/index.d.ts +3 -0
  240. package/dist/tempo/index.d.ts.map +1 -0
  241. package/dist/tempo/index.js +3 -0
  242. package/dist/tempo/index.js.map +1 -0
  243. package/dist/tempo/internal/account.d.ts +32 -0
  244. package/dist/tempo/internal/account.d.ts.map +1 -0
  245. package/dist/tempo/internal/account.js +33 -0
  246. package/dist/tempo/internal/account.js.map +1 -0
  247. package/dist/tempo/internal/defaults.d.ts +18 -0
  248. package/dist/tempo/internal/defaults.d.ts.map +1 -0
  249. package/dist/tempo/internal/defaults.js +18 -0
  250. package/dist/tempo/internal/defaults.js.map +1 -0
  251. package/dist/tempo/internal/types.d.ts +11 -0
  252. package/dist/tempo/internal/types.d.ts.map +1 -0
  253. package/dist/tempo/internal/types.js +2 -0
  254. package/dist/tempo/internal/types.js.map +1 -0
  255. package/dist/tempo/server/Charge.d.ts +77 -0
  256. package/dist/tempo/server/Charge.d.ts.map +1 -0
  257. package/dist/tempo/server/Charge.js +228 -0
  258. package/dist/tempo/server/Charge.js.map +1 -0
  259. package/dist/tempo/server/MethodIntents.d.ts +140 -0
  260. package/dist/tempo/server/MethodIntents.d.ts.map +1 -0
  261. package/dist/tempo/server/MethodIntents.js +26 -0
  262. package/dist/tempo/server/MethodIntents.js.map +1 -0
  263. package/dist/tempo/server/Session.d.ts +148 -0
  264. package/dist/tempo/server/Session.d.ts.map +1 -0
  265. package/dist/tempo/server/Session.js +529 -0
  266. package/dist/tempo/server/Session.js.map +1 -0
  267. package/dist/tempo/server/internal/transport.d.ts +47 -0
  268. package/dist/tempo/server/internal/transport.d.ts.map +1 -0
  269. package/dist/tempo/server/internal/transport.js +118 -0
  270. package/dist/tempo/server/internal/transport.js.map +1 -0
  271. package/dist/tempo/stream/Chain.d.ts +52 -0
  272. package/dist/tempo/stream/Chain.d.ts.map +1 -0
  273. package/dist/tempo/stream/Chain.js +215 -0
  274. package/dist/tempo/stream/Chain.js.map +1 -0
  275. package/dist/tempo/stream/Channel.d.ts +26 -0
  276. package/dist/tempo/stream/Channel.d.ts.map +1 -0
  277. package/dist/tempo/stream/Channel.js +27 -0
  278. package/dist/tempo/stream/Channel.js.map +1 -0
  279. package/dist/tempo/stream/ChannelStore.d.ts +103 -0
  280. package/dist/tempo/stream/ChannelStore.d.ts.map +1 -0
  281. package/dist/tempo/stream/ChannelStore.js +100 -0
  282. package/dist/tempo/stream/ChannelStore.js.map +1 -0
  283. package/dist/tempo/stream/Receipt.d.ts +22 -0
  284. package/dist/tempo/stream/Receipt.d.ts.map +1 -0
  285. package/dist/tempo/stream/Receipt.js +34 -0
  286. package/dist/tempo/stream/Receipt.js.map +1 -0
  287. package/dist/tempo/stream/Sse.d.ts +134 -0
  288. package/dist/tempo/stream/Sse.d.ts.map +1 -0
  289. package/dist/tempo/stream/Sse.js +288 -0
  290. package/dist/tempo/stream/Sse.js.map +1 -0
  291. package/dist/tempo/stream/Types.d.ts +78 -0
  292. package/dist/tempo/stream/Types.d.ts.map +1 -0
  293. package/dist/tempo/stream/Types.js +2 -0
  294. package/dist/tempo/stream/Types.js.map +1 -0
  295. package/dist/tempo/stream/Voucher.d.ts +20 -0
  296. package/dist/tempo/stream/Voucher.d.ts.map +1 -0
  297. package/dist/tempo/stream/Voucher.js +98 -0
  298. package/dist/tempo/stream/Voucher.js.map +1 -0
  299. package/dist/tempo/stream/escrow.abi.d.ts +598 -0
  300. package/dist/tempo/stream/escrow.abi.d.ts.map +1 -0
  301. package/dist/tempo/stream/escrow.abi.js +760 -0
  302. package/dist/tempo/stream/escrow.abi.js.map +1 -0
  303. package/dist/tempo/stream/index.d.ts +8 -0
  304. package/dist/tempo/stream/index.d.ts.map +1 -0
  305. package/dist/tempo/stream/index.js +8 -0
  306. package/dist/tempo/stream/index.js.map +1 -0
  307. package/dist/viem/Account.d.ts +12 -0
  308. package/dist/viem/Account.d.ts.map +1 -0
  309. package/dist/viem/Account.js +14 -0
  310. package/dist/viem/Account.js.map +1 -0
  311. package/dist/viem/Client.d.ts +21 -0
  312. package/dist/viem/Client.d.ts.map +1 -0
  313. package/dist/viem/Client.js +19 -0
  314. package/dist/viem/Client.js.map +1 -0
  315. package/dist/zod.d.ts +17 -0
  316. package/dist/zod.d.ts.map +1 -0
  317. package/dist/zod.js +35 -0
  318. package/dist/zod.js.map +1 -0
  319. package/package.json +117 -4
  320. package/src/BodyDigest.test.ts +43 -0
  321. package/src/BodyDigest.ts +53 -0
  322. package/src/Challenge.test-d.ts +81 -0
  323. package/src/Challenge.test.ts +414 -0
  324. package/src/Challenge.ts +429 -0
  325. package/src/Credential.test.ts +227 -0
  326. package/src/Credential.ts +154 -0
  327. package/src/Errors.test.ts +402 -0
  328. package/src/Errors.ts +348 -0
  329. package/src/Expires.ts +34 -0
  330. package/src/Intent.test.ts +180 -0
  331. package/src/Intent.ts +109 -0
  332. package/src/Mcp.ts +81 -0
  333. package/src/MethodIntent.test.ts +303 -0
  334. package/src/MethodIntent.ts +388 -0
  335. package/src/PaymentRequest.test.ts +152 -0
  336. package/src/PaymentRequest.ts +107 -0
  337. package/src/Receipt.test.ts +98 -0
  338. package/src/Receipt.ts +129 -0
  339. package/src/Store.ts +84 -0
  340. package/src/cli.test.ts +542 -0
  341. package/src/cli.ts +1319 -0
  342. package/src/client/Methods.ts +3 -0
  343. package/src/client/Mppx.test-d.ts +90 -0
  344. package/src/client/Mppx.test.ts +468 -0
  345. package/src/client/Mppx.ts +149 -0
  346. package/src/client/Transport.test.ts +283 -0
  347. package/src/client/Transport.ts +115 -0
  348. package/src/client/index.ts +4 -0
  349. package/src/client/internal/Fetch.test-d.ts +57 -0
  350. package/src/client/internal/Fetch.test.ts +281 -0
  351. package/src/client/internal/Fetch.ts +157 -0
  352. package/src/env.d.ts +11 -0
  353. package/src/index.ts +12 -0
  354. package/src/internal/types.ts +403 -0
  355. package/src/mcp-sdk/client/McpClient.test-d.ts +109 -0
  356. package/src/mcp-sdk/client/McpClient.test.ts +219 -0
  357. package/src/mcp-sdk/client/McpClient.ts +187 -0
  358. package/src/mcp-sdk/client/index.ts +2 -0
  359. package/src/mcp-sdk/server/Transport.ts +94 -0
  360. package/src/mcp-sdk/server/index.ts +1 -0
  361. package/src/middlewares/elysia.ts +66 -0
  362. package/src/middlewares/express.test.ts +155 -0
  363. package/src/middlewares/express.ts +82 -0
  364. package/src/middlewares/hono.test.ts +148 -0
  365. package/src/middlewares/hono.ts +62 -0
  366. package/src/middlewares/internal/mppx.ts +30 -0
  367. package/src/middlewares/nextjs.test.ts +164 -0
  368. package/src/middlewares/nextjs.ts +66 -0
  369. package/src/proxy/Proxy.test.ts +472 -0
  370. package/src/proxy/Proxy.ts +175 -0
  371. package/src/proxy/Service.test.ts +125 -0
  372. package/src/proxy/Service.ts +227 -0
  373. package/src/proxy/index.ts +6 -0
  374. package/src/proxy/internal/Headers.test.ts +100 -0
  375. package/src/proxy/internal/Headers.ts +40 -0
  376. package/src/proxy/internal/Route.test.ts +143 -0
  377. package/src/proxy/internal/Route.ts +54 -0
  378. package/src/proxy/services/anthropic.ts +45 -0
  379. package/src/proxy/services/openai.test.ts +97 -0
  380. package/src/proxy/services/openai.ts +48 -0
  381. package/src/proxy/services/stripe.ts +49 -0
  382. package/src/server/Methods.ts +2 -0
  383. package/src/server/Mppx.test-d.ts +343 -0
  384. package/src/server/Mppx.test.ts +342 -0
  385. package/src/server/Mppx.ts +378 -0
  386. package/src/server/NodeListener.test.ts +188 -0
  387. package/src/server/NodeListener.ts +3 -0
  388. package/src/server/Request.test.ts +102 -0
  389. package/src/server/Request.ts +33 -0
  390. package/src/server/Response.test.ts +31 -0
  391. package/src/server/Response.ts +27 -0
  392. package/src/server/Transport.test.ts +294 -0
  393. package/src/server/Transport.ts +222 -0
  394. package/src/server/index.ts +8 -0
  395. package/src/stripe/Charge.integration.test.ts +326 -0
  396. package/src/stripe/Intents.test.ts +52 -0
  397. package/src/stripe/Intents.ts +27 -0
  398. package/src/stripe/client/Charge.ts +119 -0
  399. package/src/stripe/client/MethodIntents.ts +37 -0
  400. package/src/stripe/client/index.ts +2 -0
  401. package/src/stripe/index.ts +1 -0
  402. package/src/stripe/server/Charge.ts +121 -0
  403. package/src/stripe/server/MethodIntents.ts +24 -0
  404. package/src/stripe/server/index.ts +2 -0
  405. package/src/tempo/Attribution.test.ts +187 -0
  406. package/src/tempo/Attribution.ts +156 -0
  407. package/src/tempo/Intents.test.ts +84 -0
  408. package/src/tempo/Intents.ts +93 -0
  409. package/src/tempo/client/ChannelOps.ts +233 -0
  410. package/src/tempo/client/Charge.ts +84 -0
  411. package/src/tempo/client/MethodIntents.ts +28 -0
  412. package/src/tempo/client/Session.ts +369 -0
  413. package/src/tempo/client/SessionManager.test.ts +223 -0
  414. package/src/tempo/client/SessionManager.ts +270 -0
  415. package/src/tempo/client/index.ts +5 -0
  416. package/src/tempo/index.ts +2 -0
  417. package/src/tempo/internal/account.ts +47 -0
  418. package/src/tempo/internal/defaults.ts +20 -0
  419. package/src/tempo/internal/types.ts +8 -0
  420. package/src/tempo/server/Charge.test.ts +847 -0
  421. package/src/tempo/server/Charge.ts +309 -0
  422. package/src/tempo/server/MethodIntents.ts +29 -0
  423. package/src/tempo/server/Session.test.ts +1349 -0
  424. package/src/tempo/server/Session.ts +773 -0
  425. package/src/tempo/server/Sse.test.ts +289 -0
  426. package/src/tempo/server/index.ts +5 -0
  427. package/src/tempo/server/internal/transport.ts +153 -0
  428. package/src/tempo/stream/Chain.ts +333 -0
  429. package/src/tempo/stream/Channel.ts +50 -0
  430. package/src/tempo/stream/ChannelStore.test.ts +473 -0
  431. package/src/tempo/stream/ChannelStore.ts +202 -0
  432. package/src/tempo/stream/Receipt.test.ts +84 -0
  433. package/src/tempo/stream/Receipt.ts +45 -0
  434. package/src/tempo/stream/Sse.test.ts +401 -0
  435. package/src/tempo/stream/Sse.ts +375 -0
  436. package/src/tempo/stream/Types.ts +86 -0
  437. package/src/tempo/stream/Voucher.test.ts +134 -0
  438. package/src/tempo/stream/Voucher.ts +123 -0
  439. package/src/tempo/stream/escrow.abi.ts +759 -0
  440. package/src/tempo/stream/index.ts +7 -0
  441. package/src/tsconfig.json +10 -0
  442. package/src/viem/Account.test.ts +71 -0
  443. package/src/viem/Account.ts +30 -0
  444. package/src/viem/Client.test.ts +58 -0
  445. package/src/viem/Client.ts +33 -0
  446. package/src/zod.ts +47 -0
@@ -0,0 +1,847 @@
1
+ import { Challenge, Credential, Receipt } from 'mppx'
2
+ import { Mppx as Mppx_client, tempo as tempo_client } from 'mppx/client'
3
+ import { Mppx as Mppx_server, tempo as tempo_server } from 'mppx/server'
4
+ import type { Hex } from 'ox'
5
+ import { Actions } from 'viem/tempo'
6
+ import { describe, expect, test } from 'vitest'
7
+ import * as Http from '~test/Http.js'
8
+ import { accounts, asset, chain, client } from '~test/tempo/viem.js'
9
+ import * as Attribution from '../Attribution.js'
10
+
11
+ const realm = 'api.example.com'
12
+ const secretKey = 'test-secret-key'
13
+
14
+ const server = Mppx_server.create({
15
+ methods: [
16
+ tempo_server.charge({
17
+ getClient() {
18
+ return client
19
+ },
20
+ currency: asset,
21
+ account: accounts[0],
22
+ }),
23
+ ],
24
+ realm,
25
+ secretKey,
26
+ })
27
+
28
+ describe('tempo', () => {
29
+ describe('intent: charge; type: hash', () => {
30
+ test('default', async () => {
31
+ const httpServer = await Http.createServer(async (req, res) => {
32
+ const result = await Mppx_server.toNodeListener(
33
+ server.charge({ amount: '1', decimals: 6 }),
34
+ )(req, res)
35
+ if (result.status === 402) return
36
+ res.end('OK')
37
+ })
38
+
39
+ const response = await fetch(httpServer.url)
40
+ expect(response.status).toBe(402)
41
+
42
+ const challenge = Challenge.fromResponse(response, {
43
+ methods: [tempo_client.charge()],
44
+ })
45
+ const request = challenge.request
46
+ expect(request.methodDetails?.chainId).toBe(chain.id)
47
+
48
+ const memo = Attribution.encode({ serverId: challenge.realm })
49
+
50
+ const { receipt } = await Actions.token.transferSync(client, {
51
+ account: accounts[1],
52
+ amount: BigInt(request.amount),
53
+ memo,
54
+ to: request.recipient as Hex.Hex,
55
+ token: request.currency as Hex.Hex,
56
+ })
57
+ const hash = receipt.transactionHash
58
+
59
+ const credential = Credential.from({
60
+ challenge,
61
+ payload: { hash, type: 'hash' as const },
62
+ })
63
+
64
+ {
65
+ const response = await fetch(httpServer.url, {
66
+ headers: { Authorization: Credential.serialize(credential) },
67
+ })
68
+ expect(response.status).toBe(200)
69
+
70
+ const receipt = Receipt.fromResponse(response)
71
+ expect({
72
+ ...receipt,
73
+ reference: '[reference]',
74
+ timestamp: '[timestamp]',
75
+ }).toMatchInlineSnapshot(`
76
+ {
77
+ "method": "tempo",
78
+ "reference": "[reference]",
79
+ "status": "success",
80
+ "timestamp": "[timestamp]",
81
+ }
82
+ `)
83
+ }
84
+
85
+ httpServer.close()
86
+ })
87
+
88
+ test('behavior: overrides', async () => {
89
+ const overrideRecipient = accounts[2].address
90
+ const overrideCurrency = asset
91
+ const overrideExpires = new Date(Date.now() + 60_000).toISOString()
92
+
93
+ const httpServer = await Http.createServer(async (req, res) => {
94
+ const result = await Mppx_server.toNodeListener(
95
+ server.charge({
96
+ amount: '1',
97
+ currency: overrideCurrency,
98
+ expires: overrideExpires,
99
+ recipient: overrideRecipient,
100
+ }),
101
+ )(req, res)
102
+ if (result.status === 402) return
103
+ res.end('OK')
104
+ })
105
+
106
+ const response = await fetch(httpServer.url)
107
+ expect(response.status).toBe(402)
108
+
109
+ const challenge = Challenge.fromResponse(response, {
110
+ methods: [tempo_client.charge()],
111
+ })
112
+ const request = challenge.request
113
+ expect(request.recipient).toBe(overrideRecipient)
114
+ expect(request.currency).toBe(overrideCurrency)
115
+ expect(request.expires).toBe(overrideExpires)
116
+
117
+ const memo = Attribution.encode({ serverId: challenge.realm })
118
+
119
+ const { receipt } = await Actions.token.transferSync(client, {
120
+ account: accounts[1],
121
+ amount: BigInt(request.amount),
122
+ memo,
123
+ to: request.recipient as Hex.Hex,
124
+ token: request.currency as Hex.Hex,
125
+ })
126
+ const hash = receipt.transactionHash
127
+
128
+ const credential = Credential.from({
129
+ challenge,
130
+ payload: { hash, type: 'hash' as const },
131
+ })
132
+
133
+ {
134
+ const response = await fetch(httpServer.url, {
135
+ headers: { Authorization: Credential.serialize(credential) },
136
+ })
137
+ expect(response.status).toBe(200)
138
+
139
+ const receipt = Receipt.fromResponse(response)
140
+ expect(receipt.status).toBe('success')
141
+ }
142
+
143
+ httpServer.close()
144
+ })
145
+
146
+ test('behavior: rejects hash with non-matching Transfer log', async () => {
147
+ const wrongRecipient = accounts[2].address
148
+
149
+ const httpServer = await Http.createServer(async (req, res) => {
150
+ const result = await Mppx_server.toNodeListener(server.charge({ amount: '1' }))(req, res)
151
+ if (result.status === 402) return
152
+ res.end('OK')
153
+ })
154
+
155
+ const response = await fetch(httpServer.url)
156
+ expect(response.status).toBe(402)
157
+
158
+ const challenge = Challenge.fromResponse(response, {
159
+ methods: [tempo_client.charge()],
160
+ })
161
+ const request = challenge.request
162
+
163
+ const { receipt } = await Actions.token.transferSync(client, {
164
+ account: accounts[1],
165
+ amount: BigInt(request.amount),
166
+ to: wrongRecipient,
167
+ token: request.currency as Hex.Hex,
168
+ })
169
+
170
+ const credential = Credential.from({
171
+ challenge,
172
+ payload: { hash: receipt.transactionHash, type: 'hash' as const },
173
+ })
174
+
175
+ {
176
+ const response = await fetch(httpServer.url, {
177
+ headers: { Authorization: Credential.serialize(credential) },
178
+ })
179
+ expect(response.status).toBe(402)
180
+ const body = (await response.json()) as { detail: string }
181
+ expect(body.detail).toContain('Payment verification failed: no matching transfer found.')
182
+ }
183
+
184
+ httpServer.close()
185
+ })
186
+
187
+ test('behavior: rejects expired request', async () => {
188
+ const httpServer = await Http.createServer(async (req, res) => {
189
+ const result = await Mppx_server.toNodeListener(
190
+ server.charge({
191
+ amount: '1',
192
+ expires: new Date(Date.now() - 1000).toISOString(),
193
+ }),
194
+ )(req, res)
195
+ if (result.status === 402) return
196
+ res.end('OK')
197
+ })
198
+
199
+ const response = await fetch(httpServer.url)
200
+ expect(response.status).toBe(402)
201
+
202
+ const challenge = Challenge.fromResponse(response, {
203
+ methods: [tempo_client.charge()],
204
+ })
205
+ const request = challenge.request
206
+
207
+ const { receipt } = await Actions.token.transferSync(client, {
208
+ account: accounts[1],
209
+ amount: BigInt(request.amount),
210
+ to: request.recipient as Hex.Hex,
211
+ token: request.currency as Hex.Hex,
212
+ })
213
+
214
+ const credential = Credential.from({
215
+ challenge,
216
+ payload: { hash: receipt.transactionHash, type: 'hash' as const },
217
+ })
218
+
219
+ {
220
+ const response = await fetch(httpServer.url, {
221
+ headers: { Authorization: Credential.serialize(credential) },
222
+ })
223
+ expect(response.status).toBe(402)
224
+ const body = (await response.json()) as { detail: string }
225
+ expect(body.detail).toMatch(/^Payment expired at /)
226
+ }
227
+
228
+ httpServer.close()
229
+ })
230
+
231
+ test('behavior: rejects when no client configured for chainId', async () => {
232
+ const server = Mppx_server.create({
233
+ methods: [
234
+ tempo_server.charge({
235
+ getClient({ chainId }: { chainId?: number | undefined }) {
236
+ if (chainId === chain.id) return client
237
+ throw new Error('not found')
238
+ },
239
+ currency: asset,
240
+ account: accounts[0],
241
+ }),
242
+ ],
243
+ realm,
244
+ secretKey,
245
+ })
246
+
247
+ const httpServer = await Http.createServer(async (req, res) => {
248
+ try {
249
+ const result = await Mppx_server.toNodeListener(
250
+ server.charge({
251
+ amount: '1',
252
+ chainId: 123456,
253
+ }),
254
+ )(req, res)
255
+ if (result.status === 402) return
256
+ res.end('OK')
257
+ } catch (e) {
258
+ res.statusCode = 500
259
+ res.end((e as Error).message)
260
+ }
261
+ })
262
+
263
+ const response = await fetch(httpServer.url)
264
+ expect(response.status).toBe(500)
265
+ expect(await response.text()).toMatchInlineSnapshot(
266
+ `"No client configured with chainId 123456."`,
267
+ )
268
+
269
+ httpServer.close()
270
+ })
271
+
272
+ test('behavior: rejects when client not configured for chainId', async () => {
273
+ const httpServer = await Http.createServer(async (req, res) => {
274
+ try {
275
+ const result = await Mppx_server.toNodeListener(
276
+ server.charge({
277
+ amount: '1',
278
+ chainId: 999999,
279
+ }),
280
+ )(req, res)
281
+ if (result.status === 402) return
282
+ res.end('OK')
283
+ } catch (e) {
284
+ res.statusCode = 500
285
+ res.end((e as Error).message)
286
+ }
287
+ })
288
+
289
+ const response = await fetch(httpServer.url)
290
+ expect(response.status).toBe(500)
291
+ expect(await response.text()).toMatchInlineSnapshot(
292
+ `"Client not configured with chainId 999999."`,
293
+ )
294
+
295
+ httpServer.close()
296
+ })
297
+ })
298
+
299
+ describe('intent: charge; type: transaction; via Mppx', () => {
300
+ test('default', async () => {
301
+ const mppx = Mppx_client.create({
302
+ polyfill: false,
303
+ methods: [
304
+ tempo_client({
305
+ account: accounts[1],
306
+ getClient() {
307
+ return client
308
+ },
309
+ }),
310
+ ],
311
+ })
312
+
313
+ const httpServer = await Http.createServer(async (req, res) => {
314
+ const result = await Mppx_server.toNodeListener(
315
+ server.charge({
316
+ amount: '1',
317
+ currency: asset,
318
+ recipient: accounts[0].address,
319
+ }),
320
+ )(req, res)
321
+ if (result.status === 402) return
322
+ res.end('OK')
323
+ })
324
+
325
+ const response = await fetch(httpServer.url)
326
+ expect(response.status).toBe(402)
327
+
328
+ const credential = await mppx.createCredential(response)
329
+
330
+ {
331
+ const response = await fetch(httpServer.url, {
332
+ headers: { Authorization: credential },
333
+ })
334
+ expect(response.status).toBe(200)
335
+
336
+ const receipt = Receipt.fromResponse(response)
337
+ expect({
338
+ ...receipt,
339
+ reference: '[reference]',
340
+ timestamp: '[timestamp]',
341
+ }).toMatchInlineSnapshot(`
342
+ {
343
+ "method": "tempo",
344
+ "reference": "[reference]",
345
+ "status": "success",
346
+ "timestamp": "[timestamp]",
347
+ }
348
+ `)
349
+ }
350
+
351
+ httpServer.close()
352
+ })
353
+
354
+ test('behavior: overrides', async () => {
355
+ const overrideRecipient = accounts[2].address
356
+ const overrideCurrency = asset
357
+ const overrideExpires = new Date(Date.now() + 60_000).toISOString()
358
+
359
+ const mppx = Mppx_client.create({
360
+ polyfill: false,
361
+ methods: [
362
+ tempo_client({
363
+ account: accounts[1],
364
+ getClient() {
365
+ return client
366
+ },
367
+ }),
368
+ ],
369
+ })
370
+
371
+ const httpServer = await Http.createServer(async (req, res) => {
372
+ const result = await Mppx_server.toNodeListener(
373
+ server.charge({
374
+ amount: '1',
375
+ currency: overrideCurrency,
376
+ expires: overrideExpires,
377
+ recipient: overrideRecipient,
378
+ }),
379
+ )(req, res)
380
+ if (result.status === 402) return
381
+ res.end('OK')
382
+ })
383
+
384
+ const response = await fetch(httpServer.url)
385
+ expect(response.status).toBe(402)
386
+
387
+ const credential = await mppx.createCredential(response)
388
+
389
+ {
390
+ const response = await fetch(httpServer.url, {
391
+ headers: { Authorization: credential },
392
+ })
393
+ expect(response.status).toBe(200)
394
+
395
+ const receipt = Receipt.fromResponse(response)
396
+ expect(receipt.status).toBe('success')
397
+ }
398
+
399
+ httpServer.close()
400
+ })
401
+
402
+ test('behavior: fee payer', async () => {
403
+ const mppx = Mppx_client.create({
404
+ polyfill: false,
405
+ methods: [
406
+ tempo_client({
407
+ account: accounts[1],
408
+ getClient() {
409
+ return client
410
+ },
411
+ }),
412
+ ],
413
+ })
414
+
415
+ const httpServer = await Http.createServer(async (req, res) => {
416
+ const result = await Mppx_server.toNodeListener(
417
+ server.charge({
418
+ feePayer: accounts[0],
419
+ amount: '1',
420
+ currency: asset,
421
+ recipient: accounts[0].address,
422
+ }),
423
+ )(req, res)
424
+ if (result.status === 402) return
425
+ res.end('OK')
426
+ })
427
+
428
+ const response = await fetch(httpServer.url)
429
+ expect(response.status).toBe(402)
430
+
431
+ const credential = await mppx.createCredential(response)
432
+
433
+ {
434
+ const response = await fetch(httpServer.url, {
435
+ headers: { Authorization: credential },
436
+ })
437
+ expect(response.status).toBe(200)
438
+
439
+ const receipt = Receipt.fromResponse(response)
440
+ expect({
441
+ ...receipt,
442
+ reference: '[reference]',
443
+ timestamp: '[timestamp]',
444
+ }).toMatchInlineSnapshot(`
445
+ {
446
+ "method": "tempo",
447
+ "reference": "[reference]",
448
+ "status": "success",
449
+ "timestamp": "[timestamp]",
450
+ }
451
+ `)
452
+ }
453
+
454
+ httpServer.close()
455
+ })
456
+
457
+ test('behavior: fee payer (hoisted)', async () => {
458
+ const mppx = Mppx_client.create({
459
+ polyfill: false,
460
+ methods: [
461
+ tempo_client({
462
+ account: accounts[1],
463
+ getClient() {
464
+ return client
465
+ },
466
+ }),
467
+ ],
468
+ })
469
+
470
+ const server = Mppx_server.create({
471
+ methods: [
472
+ tempo_server.charge({
473
+ account: accounts[0],
474
+ getClient() {
475
+ return client
476
+ },
477
+ currency: asset,
478
+ feePayer: true,
479
+ }),
480
+ ],
481
+ realm,
482
+ secretKey,
483
+ })
484
+
485
+ const httpServer = await Http.createServer(async (req, res) => {
486
+ const result = await Mppx_server.toNodeListener(
487
+ server.charge({
488
+ amount: '1',
489
+ currency: asset,
490
+ recipient: accounts[0].address,
491
+ }),
492
+ )(req, res)
493
+ if (result.status === 402) return
494
+ res.end('OK')
495
+ })
496
+
497
+ const response = await fetch(httpServer.url)
498
+ expect(response.status).toBe(402)
499
+
500
+ const credential = await mppx.createCredential(response)
501
+
502
+ {
503
+ const response = await fetch(httpServer.url, {
504
+ headers: { Authorization: credential },
505
+ })
506
+ expect(response.status).toBe(200)
507
+
508
+ const receipt = Receipt.fromResponse(response)
509
+ expect({
510
+ ...receipt,
511
+ reference: '[reference]',
512
+ timestamp: '[timestamp]',
513
+ }).toMatchInlineSnapshot(`
514
+ {
515
+ "method": "tempo",
516
+ "reference": "[reference]",
517
+ "status": "success",
518
+ "timestamp": "[timestamp]",
519
+ }
520
+ `)
521
+ }
522
+
523
+ httpServer.close()
524
+ })
525
+ })
526
+
527
+ describe('intent: unknown', () => {
528
+ test('behavior: returns 402 for invalid payload schema', async () => {
529
+ const httpServer = await Http.createServer(async (req, res) => {
530
+ const result = await Mppx_server.toNodeListener(
531
+ server.charge({
532
+ amount: '1',
533
+ expires: new Date(Date.now() + 60_000).toISOString(),
534
+ }),
535
+ )(req, res)
536
+ if (result.status === 402) return
537
+ res.end('OK')
538
+ })
539
+
540
+ const response = await fetch(httpServer.url)
541
+ expect(response.status).toBe(402)
542
+
543
+ const challenge = Challenge.fromResponse(response)
544
+
545
+ const credential = Credential.from({
546
+ challenge,
547
+ payload: { type: 'unknown' as never },
548
+ })
549
+
550
+ {
551
+ const response = await fetch(httpServer.url, {
552
+ headers: { Authorization: Credential.serialize(credential) },
553
+ })
554
+ expect(response.status).toBe(402)
555
+ }
556
+
557
+ httpServer.close()
558
+ })
559
+ })
560
+
561
+ describe('recipient/feePayer resolution', () => {
562
+ test('recipient: string resolves to string', () => {
563
+ const method = tempo_server.charge({
564
+ getClient: () => client,
565
+ account: accounts[0].address,
566
+ })
567
+ expect(method.defaults?.recipient).toBe(accounts[0].address)
568
+ })
569
+
570
+ test('recipient: Account resolves to account.address', () => {
571
+ const method = tempo_server.charge({
572
+ getClient: () => client,
573
+ account: accounts[0],
574
+ })
575
+ expect(method.defaults?.recipient).toBe(accounts[0].address)
576
+ })
577
+
578
+ test('recipient: Account with feePayer: true resolves feePayer', async () => {
579
+ const mppx = Mppx_client.create({
580
+ polyfill: false,
581
+ methods: [
582
+ tempo_client.charge({
583
+ account: accounts[1],
584
+ getClient: () => client,
585
+ }),
586
+ ],
587
+ })
588
+
589
+ const server_ = Mppx_server.create({
590
+ methods: [
591
+ tempo_server.charge({
592
+ account: accounts[0],
593
+ getClient: () => client,
594
+ currency: asset,
595
+ feePayer: true,
596
+ }),
597
+ ],
598
+ realm,
599
+ secretKey,
600
+ })
601
+
602
+ const httpServer = await Http.createServer(async (req, res) => {
603
+ const result = await Mppx_server.toNodeListener(server_.charge({ amount: '1' }))(req, res)
604
+ if (result.status === 402) return
605
+ res.end('OK')
606
+ })
607
+
608
+ const response = await mppx.fetch(httpServer.url)
609
+ expect(response.status).toBe(200)
610
+
611
+ httpServer.close()
612
+ })
613
+
614
+ test('recipient: string with feePayer: Account resolves separately', async () => {
615
+ const mppx = Mppx_client.create({
616
+ polyfill: false,
617
+ methods: [
618
+ tempo_client.charge({
619
+ account: accounts[1],
620
+ getClient: () => client,
621
+ }),
622
+ ],
623
+ })
624
+
625
+ const server_ = Mppx_server.create({
626
+ methods: [
627
+ tempo_server.charge({
628
+ getClient: () => client,
629
+ currency: asset,
630
+ account: accounts[0].address,
631
+ feePayer: accounts[0],
632
+ }),
633
+ ],
634
+ realm,
635
+ secretKey,
636
+ })
637
+
638
+ const httpServer = await Http.createServer(async (req, res) => {
639
+ const result = await Mppx_server.toNodeListener(server_.charge({ amount: '1' }))(req, res)
640
+ if (result.status === 402) return
641
+ res.end('OK')
642
+ })
643
+
644
+ const response = await mppx.fetch(httpServer.url)
645
+ expect(response.status).toBe(200)
646
+
647
+ httpServer.close()
648
+ })
649
+
650
+ test('no feePayer resolves to undefined', () => {
651
+ const method = tempo_server.charge({
652
+ getClient: () => client,
653
+ account: accounts[0].address,
654
+ })
655
+ expect(method.defaults?.recipient).toBe(accounts[0].address)
656
+ })
657
+ })
658
+
659
+ describe('attribution memo', () => {
660
+ test('client always generates attribution memo (hash credential)', async () => {
661
+ const httpServer = await Http.createServer(async (req, res) => {
662
+ const result = await Mppx_server.toNodeListener(
663
+ server.charge({ amount: '1', decimals: 6 }),
664
+ )(req, res)
665
+ if (result.status === 402) return
666
+ res.end('OK')
667
+ })
668
+
669
+ const response = await fetch(httpServer.url)
670
+ expect(response.status).toBe(402)
671
+
672
+ const challenge = Challenge.fromResponse(response, {
673
+ methods: [tempo_client.charge()],
674
+ })
675
+
676
+ expect(challenge.request.methodDetails?.memo).toBeUndefined()
677
+
678
+ const memo = Attribution.encode({ serverId: challenge.realm, clientId: 'test-app' })
679
+ expect(Attribution.isMppMemo(memo)).toBe(true)
680
+ expect(Attribution.verifyServer(memo, realm)).toBe(true)
681
+
682
+ const { receipt } = await Actions.token.transferSync(client, {
683
+ account: accounts[1],
684
+ amount: BigInt(challenge.request.amount),
685
+ memo: memo as Hex.Hex,
686
+ to: challenge.request.recipient as Hex.Hex,
687
+ token: challenge.request.currency as Hex.Hex,
688
+ })
689
+
690
+ const credential = Credential.from({
691
+ challenge,
692
+ payload: { hash: receipt.transactionHash, type: 'hash' as const },
693
+ })
694
+
695
+ {
696
+ const response = await fetch(httpServer.url, {
697
+ headers: { Authorization: Credential.serialize(credential) },
698
+ })
699
+ expect(response.status).toBe(200)
700
+
701
+ const paymentReceipt = Receipt.fromResponse(response)
702
+ expect(paymentReceipt.status).toBe('success')
703
+ }
704
+
705
+ httpServer.close()
706
+ })
707
+
708
+ test('anonymous client (no clientId) generates valid attribution memo', async () => {
709
+ const httpServer = await Http.createServer(async (req, res) => {
710
+ const result = await Mppx_server.toNodeListener(
711
+ server.charge({ amount: '1', decimals: 6 }),
712
+ )(req, res)
713
+ if (result.status === 402) return
714
+ res.end('OK')
715
+ })
716
+
717
+ const response = await fetch(httpServer.url)
718
+ expect(response.status).toBe(402)
719
+
720
+ const challenge = Challenge.fromResponse(response, {
721
+ methods: [tempo_client.charge()],
722
+ })
723
+
724
+ const memo = Attribution.encode({ serverId: challenge.realm })
725
+ const decoded = Attribution.decode(memo)
726
+ expect(decoded).not.toBeNull()
727
+ expect(decoded!.clientFingerprint).toBeNull()
728
+
729
+ const { receipt } = await Actions.token.transferSync(client, {
730
+ account: accounts[1],
731
+ amount: BigInt(challenge.request.amount),
732
+ memo: memo as Hex.Hex,
733
+ to: challenge.request.recipient as Hex.Hex,
734
+ token: challenge.request.currency as Hex.Hex,
735
+ })
736
+
737
+ const credential = Credential.from({
738
+ challenge,
739
+ payload: { hash: receipt.transactionHash, type: 'hash' as const },
740
+ })
741
+
742
+ {
743
+ const response = await fetch(httpServer.url, {
744
+ headers: { Authorization: Credential.serialize(credential) },
745
+ })
746
+ expect(response.status).toBe(200)
747
+ }
748
+
749
+ httpServer.close()
750
+ })
751
+
752
+ test('client generates memo for transaction credential via Mppx', async () => {
753
+ const mppx = Mppx_client.create({
754
+ polyfill: false,
755
+ methods: [
756
+ tempo_client({
757
+ account: accounts[1],
758
+ clientId: 'test-app',
759
+ getClient() {
760
+ return client
761
+ },
762
+ }),
763
+ ],
764
+ })
765
+
766
+ const httpServer = await Http.createServer(async (req, res) => {
767
+ const result = await Mppx_server.toNodeListener(
768
+ server.charge({
769
+ amount: '1',
770
+ currency: asset,
771
+ recipient: accounts[0].address,
772
+ }),
773
+ )(req, res)
774
+ if (result.status === 402) return
775
+ res.end('OK')
776
+ })
777
+
778
+ const response = await mppx.fetch(httpServer.url)
779
+ expect(response.status).toBe(200)
780
+
781
+ httpServer.close()
782
+ })
783
+
784
+ test('server accepts plain transfer without memo', async () => {
785
+ const httpServer = await Http.createServer(async (req, res) => {
786
+ const result = await Mppx_server.toNodeListener(
787
+ server.charge({ amount: '1', decimals: 6 }),
788
+ )(req, res)
789
+ if (result.status === 402) return
790
+ res.end('OK')
791
+ })
792
+
793
+ const response = await fetch(httpServer.url)
794
+ expect(response.status).toBe(402)
795
+
796
+ const challenge = Challenge.fromResponse(response, {
797
+ methods: [tempo_client.charge()],
798
+ })
799
+
800
+ const { receipt } = await Actions.token.transferSync(client, {
801
+ account: accounts[1],
802
+ amount: BigInt(challenge.request.amount),
803
+ to: challenge.request.recipient as Hex.Hex,
804
+ token: challenge.request.currency as Hex.Hex,
805
+ })
806
+
807
+ const credential = Credential.from({
808
+ challenge,
809
+ payload: { hash: receipt.transactionHash, type: 'hash' as const },
810
+ })
811
+
812
+ {
813
+ const response = await fetch(httpServer.url, {
814
+ headers: { Authorization: Credential.serialize(credential) },
815
+ })
816
+ expect(response.status).toBe(200)
817
+ }
818
+
819
+ httpServer.close()
820
+ })
821
+
822
+ test('user-provided memo takes priority over attribution', async () => {
823
+ const userMemo =
824
+ '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' as `0x${string}`
825
+
826
+ const httpServer = await Http.createServer(async (req, res) => {
827
+ const result = await Mppx_server.toNodeListener(
828
+ server.charge({ amount: '1', decimals: 6, memo: userMemo }),
829
+ )(req, res)
830
+ if (result.status === 402) return
831
+ res.end('OK')
832
+ })
833
+
834
+ const response = await fetch(httpServer.url)
835
+ expect(response.status).toBe(402)
836
+
837
+ const challenge = Challenge.fromResponse(response, {
838
+ methods: [tempo_client.charge()],
839
+ })
840
+ const memo = challenge.request.methodDetails?.memo as `0x${string}` | undefined
841
+ expect(memo).toBe(userMemo)
842
+ expect(Attribution.isMppMemo(memo!)).toBe(false)
843
+
844
+ httpServer.close()
845
+ })
846
+ })
847
+ })