mppx 0.1.1 → 0.2.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 (219) hide show
  1. package/dist/Challenge.d.ts +16 -16
  2. package/dist/Challenge.d.ts.map +1 -1
  3. package/dist/Challenge.js +7 -7
  4. package/dist/Challenge.js.map +1 -1
  5. package/dist/Errors.d.ts +58 -8
  6. package/dist/Errors.d.ts.map +1 -1
  7. package/dist/Errors.js +51 -9
  8. package/dist/Errors.js.map +1 -1
  9. package/dist/Method.d.ts +154 -0
  10. package/dist/Method.d.ts.map +1 -0
  11. package/dist/Method.js +81 -0
  12. package/dist/Method.js.map +1 -0
  13. package/dist/PaymentRequest.d.ts +5 -5
  14. package/dist/PaymentRequest.d.ts.map +1 -1
  15. package/dist/PaymentRequest.js +5 -5
  16. package/dist/cli.js +67 -18
  17. package/dist/cli.js.map +1 -1
  18. package/dist/client/Methods.d.ts +2 -2
  19. package/dist/client/Methods.d.ts.map +1 -1
  20. package/dist/client/Methods.js +2 -2
  21. package/dist/client/Methods.js.map +1 -1
  22. package/dist/client/Mppx.d.ts +7 -7
  23. package/dist/client/Mppx.d.ts.map +1 -1
  24. package/dist/client/Mppx.js +3 -3
  25. package/dist/client/Mppx.js.map +1 -1
  26. package/dist/client/internal/Fetch.d.ts +10 -10
  27. package/dist/client/internal/Fetch.d.ts.map +1 -1
  28. package/dist/client/internal/Fetch.js +2 -2
  29. package/dist/client/internal/Fetch.js.map +1 -1
  30. package/dist/index.d.ts +1 -2
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1 -2
  33. package/dist/index.js.map +1 -1
  34. package/dist/mcp-sdk/client/McpClient.d.ts +6 -6
  35. package/dist/mcp-sdk/client/McpClient.d.ts.map +1 -1
  36. package/dist/mcp-sdk/client/McpClient.js +4 -4
  37. package/dist/mcp-sdk/client/McpClient.js.map +1 -1
  38. package/dist/middlewares/elysia.d.ts +1 -1
  39. package/dist/middlewares/express.d.ts +1 -1
  40. package/dist/middlewares/hono.d.ts +1 -1
  41. package/dist/middlewares/internal/mppx.d.ts +7 -7
  42. package/dist/middlewares/internal/mppx.d.ts.map +1 -1
  43. package/dist/middlewares/internal/mppx.js +5 -5
  44. package/dist/middlewares/internal/mppx.js.map +1 -1
  45. package/dist/middlewares/nextjs.d.ts +1 -1
  46. package/dist/proxy/Service.js +2 -2
  47. package/dist/proxy/Service.js.map +1 -1
  48. package/dist/server/Methods.d.ts +2 -2
  49. package/dist/server/Methods.d.ts.map +1 -1
  50. package/dist/server/Methods.js +2 -2
  51. package/dist/server/Methods.js.map +1 -1
  52. package/dist/server/Mppx.d.ts +17 -17
  53. package/dist/server/Mppx.d.ts.map +1 -1
  54. package/dist/server/Mppx.js +9 -9
  55. package/dist/server/Mppx.js.map +1 -1
  56. package/dist/stripe/{Intents.d.ts → Methods.d.ts} +22 -22
  57. package/dist/stripe/Methods.d.ts.map +1 -0
  58. package/dist/stripe/Methods.js +42 -0
  59. package/dist/stripe/Methods.js.map +1 -0
  60. package/dist/stripe/client/Charge.d.ts +40 -27
  61. package/dist/stripe/client/Charge.d.ts.map +1 -1
  62. package/dist/stripe/client/Charge.js +15 -7
  63. package/dist/stripe/client/Charge.js.map +1 -1
  64. package/dist/stripe/client/{MethodIntents.d.ts → Methods.d.ts} +24 -23
  65. package/dist/stripe/client/Methods.d.ts.map +1 -0
  66. package/dist/stripe/client/{MethodIntents.js → Methods.js} +3 -3
  67. package/dist/stripe/client/Methods.js.map +1 -0
  68. package/dist/stripe/client/index.d.ts +1 -1
  69. package/dist/stripe/client/index.d.ts.map +1 -1
  70. package/dist/stripe/client/index.js +1 -1
  71. package/dist/stripe/client/index.js.map +1 -1
  72. package/dist/stripe/index.d.ts +1 -1
  73. package/dist/stripe/index.d.ts.map +1 -1
  74. package/dist/stripe/index.js +1 -1
  75. package/dist/stripe/index.js.map +1 -1
  76. package/dist/stripe/internal/types.d.ts +25 -0
  77. package/dist/stripe/internal/types.d.ts.map +1 -0
  78. package/dist/stripe/internal/types.js +2 -0
  79. package/dist/stripe/internal/types.js.map +1 -0
  80. package/dist/stripe/server/Charge.d.ts +47 -28
  81. package/dist/stripe/server/Charge.d.ts.map +1 -1
  82. package/dist/stripe/server/Charge.js +90 -32
  83. package/dist/stripe/server/Charge.js.map +1 -1
  84. package/dist/stripe/server/{MethodIntents.d.ts → Methods.d.ts} +24 -23
  85. package/dist/stripe/server/Methods.d.ts.map +1 -0
  86. package/dist/stripe/server/{MethodIntents.js → Methods.js} +3 -3
  87. package/dist/stripe/server/Methods.js.map +1 -0
  88. package/dist/stripe/server/index.d.ts +1 -1
  89. package/dist/stripe/server/index.d.ts.map +1 -1
  90. package/dist/stripe/server/index.js +1 -1
  91. package/dist/stripe/server/index.js.map +1 -1
  92. package/dist/tempo/{Intents.d.ts → Methods.d.ts} +72 -69
  93. package/dist/tempo/Methods.d.ts.map +1 -0
  94. package/dist/tempo/Methods.js +118 -0
  95. package/dist/tempo/Methods.js.map +1 -0
  96. package/dist/tempo/client/ChannelOps.d.ts +1 -1
  97. package/dist/tempo/client/ChannelOps.js +1 -1
  98. package/dist/tempo/client/Charge.d.ts +25 -25
  99. package/dist/tempo/client/Charge.d.ts.map +1 -1
  100. package/dist/tempo/client/Charge.js +3 -3
  101. package/dist/tempo/client/Charge.js.map +1 -1
  102. package/dist/tempo/client/{MethodIntents.d.ts → Methods.d.ts} +74 -70
  103. package/dist/tempo/client/Methods.d.ts.map +1 -0
  104. package/dist/tempo/client/{MethodIntents.js → Methods.js} +3 -3
  105. package/dist/tempo/client/Methods.js.map +1 -0
  106. package/dist/tempo/client/Session.d.ts +49 -45
  107. package/dist/tempo/client/Session.d.ts.map +1 -1
  108. package/dist/tempo/client/Session.js +4 -4
  109. package/dist/tempo/client/Session.js.map +1 -1
  110. package/dist/tempo/client/SessionManager.d.ts +1 -1
  111. package/dist/tempo/client/SessionManager.js +1 -1
  112. package/dist/tempo/client/index.d.ts +1 -1
  113. package/dist/tempo/client/index.d.ts.map +1 -1
  114. package/dist/tempo/client/index.js +1 -1
  115. package/dist/tempo/client/index.js.map +1 -1
  116. package/dist/tempo/index.d.ts +1 -1
  117. package/dist/tempo/index.d.ts.map +1 -1
  118. package/dist/tempo/index.js +1 -1
  119. package/dist/tempo/index.js.map +1 -1
  120. package/dist/tempo/server/Charge.d.ts +27 -27
  121. package/dist/tempo/server/Charge.d.ts.map +1 -1
  122. package/dist/tempo/server/Charge.js +3 -3
  123. package/dist/tempo/server/Charge.js.map +1 -1
  124. package/dist/tempo/server/{MethodIntents.d.ts → Methods.d.ts} +73 -69
  125. package/dist/tempo/server/Methods.d.ts.map +1 -0
  126. package/dist/tempo/server/{MethodIntents.js → Methods.js} +4 -4
  127. package/dist/tempo/server/Methods.js.map +1 -0
  128. package/dist/tempo/server/Session.d.ts +51 -47
  129. package/dist/tempo/server/Session.d.ts.map +1 -1
  130. package/dist/tempo/server/Session.js +4 -4
  131. package/dist/tempo/server/Session.js.map +1 -1
  132. package/dist/tempo/server/index.d.ts +6 -0
  133. package/dist/tempo/server/index.d.ts.map +1 -0
  134. package/dist/tempo/server/index.js +6 -0
  135. package/dist/tempo/server/index.js.map +1 -0
  136. package/package.json +1 -1
  137. package/src/Challenge.test-d.ts +3 -3
  138. package/src/Challenge.test.ts +6 -6
  139. package/src/Challenge.ts +32 -32
  140. package/src/Errors.test.ts +75 -21
  141. package/src/Errors.ts +74 -9
  142. package/src/Method.test.ts +76 -0
  143. package/src/Method.ts +228 -0
  144. package/src/PaymentRequest.test.ts +4 -4
  145. package/src/PaymentRequest.ts +9 -9
  146. package/src/cli.test.ts +12 -22
  147. package/src/cli.ts +74 -21
  148. package/src/client/Methods.ts +2 -2
  149. package/src/client/Mppx.test-d.ts +6 -6
  150. package/src/client/Mppx.test.ts +26 -22
  151. package/src/client/Mppx.ts +10 -10
  152. package/src/client/Transport.test.ts +2 -2
  153. package/src/client/internal/Fetch.ts +21 -24
  154. package/src/index.ts +1 -2
  155. package/src/mcp-sdk/client/McpClient.ts +11 -13
  156. package/src/middlewares/elysia.ts +1 -1
  157. package/src/middlewares/express.ts +1 -1
  158. package/src/middlewares/hono.ts +1 -1
  159. package/src/middlewares/internal/mppx.ts +10 -10
  160. package/src/middlewares/nextjs.ts +1 -1
  161. package/src/proxy/Service.ts +2 -2
  162. package/src/server/Methods.ts +2 -2
  163. package/src/server/Mppx.test-d.ts +27 -29
  164. package/src/server/Mppx.test.ts +23 -19
  165. package/src/server/Mppx.ts +43 -43
  166. package/src/server/Transport.test.ts +3 -3
  167. package/src/stripe/{Intents.test.ts → Methods.test.ts} +12 -12
  168. package/src/stripe/Methods.ts +45 -0
  169. package/src/stripe/client/Charge.test.ts +189 -0
  170. package/src/stripe/client/Charge.ts +29 -16
  171. package/src/stripe/client/{MethodIntents.ts → Methods.ts} +2 -2
  172. package/src/stripe/client/index.ts +1 -1
  173. package/src/stripe/index.ts +1 -1
  174. package/src/stripe/internal/types.ts +22 -0
  175. package/src/stripe/server/Charge.test.ts +241 -0
  176. package/src/stripe/server/Charge.ts +124 -38
  177. package/src/stripe/server/{MethodIntents.ts → Methods.ts} +2 -2
  178. package/src/stripe/server/index.ts +1 -1
  179. package/src/tempo/{Intents.test.ts → Methods.test.ts} +15 -15
  180. package/src/tempo/{Intents.ts → Methods.ts} +77 -22
  181. package/src/tempo/client/ChannelOps.ts +1 -1
  182. package/src/tempo/client/Charge.ts +3 -3
  183. package/src/tempo/client/{MethodIntents.ts → Methods.ts} +2 -2
  184. package/src/tempo/client/Session.ts +4 -4
  185. package/src/tempo/client/SessionManager.ts +1 -1
  186. package/src/tempo/client/index.ts +1 -1
  187. package/src/tempo/index.ts +1 -1
  188. package/src/tempo/server/Charge.ts +4 -7
  189. package/src/tempo/server/{MethodIntents.ts → Methods.ts} +3 -3
  190. package/src/tempo/server/Session.test.ts +4 -7
  191. package/src/tempo/server/Session.ts +6 -6
  192. package/src/tempo/server/index.ts +1 -1
  193. package/dist/Intent.d.ts +0 -101
  194. package/dist/Intent.d.ts.map +0 -1
  195. package/dist/Intent.js +0 -83
  196. package/dist/Intent.js.map +0 -1
  197. package/dist/MethodIntent.d.ts +0 -225
  198. package/dist/MethodIntent.d.ts.map +0 -1
  199. package/dist/MethodIntent.js +0 -156
  200. package/dist/MethodIntent.js.map +0 -1
  201. package/dist/stripe/Intents.d.ts.map +0 -1
  202. package/dist/stripe/Intents.js +0 -27
  203. package/dist/stripe/Intents.js.map +0 -1
  204. package/dist/stripe/client/MethodIntents.d.ts.map +0 -1
  205. package/dist/stripe/client/MethodIntents.js.map +0 -1
  206. package/dist/stripe/server/MethodIntents.d.ts.map +0 -1
  207. package/dist/stripe/server/MethodIntents.js.map +0 -1
  208. package/dist/tempo/Intents.d.ts.map +0 -1
  209. package/dist/tempo/Intents.js +0 -81
  210. package/dist/tempo/Intents.js.map +0 -1
  211. package/dist/tempo/client/MethodIntents.d.ts.map +0 -1
  212. package/dist/tempo/client/MethodIntents.js.map +0 -1
  213. package/dist/tempo/server/MethodIntents.d.ts.map +0 -1
  214. package/dist/tempo/server/MethodIntents.js.map +0 -1
  215. package/src/Intent.test.ts +0 -180
  216. package/src/Intent.ts +0 -109
  217. package/src/MethodIntent.test.ts +0 -303
  218. package/src/MethodIntent.ts +0 -388
  219. package/src/stripe/Intents.ts +0 -27
package/src/Errors.ts CHANGED
@@ -5,6 +5,9 @@ export abstract class PaymentError extends Error {
5
5
  /** RFC 9457 Problem Details type URI. */
6
6
  abstract readonly type: string
7
7
 
8
+ /** Human-readable summary for RFC 9457 Problem Details. */
9
+ abstract readonly title: string
10
+
8
11
  /** HTTP status code. */
9
12
  readonly status: number = 402
10
13
 
@@ -12,7 +15,7 @@ export abstract class PaymentError extends Error {
12
15
  toProblemDetails(challengeId?: string): PaymentError.ProblemDetails {
13
16
  return {
14
17
  type: this.type,
15
- title: this.name,
18
+ title: this.title,
16
19
  status: this.status,
17
20
  detail: this.message,
18
21
  ...(challengeId && { challengeId }),
@@ -40,7 +43,9 @@ export declare namespace PaymentError {
40
43
  */
41
44
  export class MalformedCredentialError extends PaymentError {
42
45
  override readonly name = 'MalformedCredentialError'
43
- readonly type = 'https://tempoxyz.github.io/payment-auth-spec/problems/malformed-credential'
46
+ readonly title = 'Malformed Credential'
47
+ override readonly status = 402
48
+ readonly type = 'https://paymentauth.org/problems/malformed-credential'
44
49
 
45
50
  constructor(options: MalformedCredentialError.Options = {}) {
46
51
  const { reason } = options
@@ -60,7 +65,9 @@ export declare namespace MalformedCredentialError {
60
65
  */
61
66
  export class InvalidChallengeError extends PaymentError {
62
67
  override readonly name = 'InvalidChallengeError'
63
- readonly type = 'https://tempoxyz.github.io/payment-auth-spec/problems/invalid-challenge'
68
+ readonly title = 'Invalid Challenge'
69
+ override readonly status = 402
70
+ readonly type = 'https://paymentauth.org/problems/invalid-challenge'
64
71
 
65
72
  constructor(options: InvalidChallengeError.Options = {}) {
66
73
  const { id, reason } = options
@@ -84,7 +91,8 @@ export declare namespace InvalidChallengeError {
84
91
  */
85
92
  export class VerificationFailedError extends PaymentError {
86
93
  override readonly name = 'VerificationFailedError'
87
- readonly type = 'https://tempoxyz.github.io/payment-auth-spec/problems/verification-failed'
94
+ readonly title = 'Verification Failed'
95
+ readonly type = 'https://paymentauth.org/problems/verification-failed'
88
96
 
89
97
  constructor(options: VerificationFailedError.Options = {}) {
90
98
  const { reason } = options
@@ -104,7 +112,8 @@ export declare namespace VerificationFailedError {
104
112
  */
105
113
  export class PaymentActionRequiredError extends PaymentError {
106
114
  override readonly name = 'PaymentActionRequiredError'
107
- readonly type = 'https://tempoxyz.github.io/payment-auth-spec/problems/payment-action-required'
115
+ readonly title = 'Payment Action Required'
116
+ readonly type = 'https://paymentauth.org/problems/payment-action-required'
108
117
 
109
118
  constructor(options: PaymentActionRequiredError.Options = {}) {
110
119
  const { reason } = options
@@ -124,7 +133,8 @@ export declare namespace PaymentActionRequiredError {
124
133
  */
125
134
  export class PaymentExpiredError extends PaymentError {
126
135
  override readonly name = 'PaymentExpiredError'
127
- readonly type = 'https://tempoxyz.github.io/payment-auth-spec/problems/payment-expired'
136
+ readonly title = 'Payment Expired'
137
+ readonly type = 'https://paymentauth.org/problems/payment-expired'
128
138
 
129
139
  constructor(options: PaymentExpiredError.Options = {}) {
130
140
  const { expires } = options
@@ -144,7 +154,8 @@ export declare namespace PaymentExpiredError {
144
154
  */
145
155
  export class PaymentRequiredError extends PaymentError {
146
156
  override readonly name = 'PaymentRequiredError'
147
- readonly type = 'https://tempoxyz.github.io/payment-auth-spec/problems/payment-required'
157
+ readonly title = 'Payment Required'
158
+ readonly type = 'https://paymentauth.org/problems/payment-required'
148
159
 
149
160
  constructor(options: PaymentRequiredError.Options = {}) {
150
161
  const { description, realm } = options
@@ -169,7 +180,8 @@ export declare namespace PaymentRequiredError {
169
180
  */
170
181
  export class InvalidPayloadError extends PaymentError {
171
182
  override readonly name = 'InvalidPayloadError'
172
- readonly type = 'https://tempoxyz.github.io/payment-auth-spec/problems/invalid-payload'
183
+ readonly title = 'Invalid Payload'
184
+ readonly type = 'https://paymentauth.org/problems/invalid-payload'
173
185
 
174
186
  constructor(options: InvalidPayloadError.Options = {}) {
175
187
  const { reason } = options
@@ -189,8 +201,9 @@ export declare namespace InvalidPayloadError {
189
201
  */
190
202
  export class BadRequestError extends PaymentError {
191
203
  override readonly name = 'BadRequestError'
204
+ readonly title = 'Bad Request'
192
205
  override readonly status = 400
193
- readonly type = 'https://tempoxyz.github.io/payment-auth-spec/problems/bad-request'
206
+ readonly type = 'https://paymentauth.org/problems/bad-request'
194
207
 
195
208
  constructor(options: BadRequestError.Options = {}) {
196
209
  const { reason } = options
@@ -205,11 +218,57 @@ export declare namespace BadRequestError {
205
218
  }
206
219
  }
207
220
 
221
+ /**
222
+ * Payment amount is insufficient (too low).
223
+ */
224
+ export class PaymentInsufficientError extends PaymentError {
225
+ override readonly name = 'PaymentInsufficientError'
226
+ readonly title = 'Payment Insufficient'
227
+ readonly type = 'https://paymentauth.org/problems/payment-insufficient'
228
+
229
+ constructor(options: PaymentInsufficientError.Options = {}) {
230
+ const { reason } = options
231
+ super(reason ? `Payment insufficient: ${reason}.` : 'Payment amount is insufficient.')
232
+ }
233
+ }
234
+
235
+ export declare namespace PaymentInsufficientError {
236
+ type Options = {
237
+ /** Reason the payment is insufficient (e.g., "expected 1000, received 500"). */
238
+ reason?: string
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Payment method is not supported by the server.
244
+ */
245
+ export class PaymentMethodUnsupportedError extends PaymentError {
246
+ override readonly name = 'PaymentMethodUnsupportedError'
247
+ readonly title = 'Method Unsupported'
248
+ override readonly status = 400
249
+ readonly type = 'https://paymentauth.org/problems/method-unsupported'
250
+
251
+ constructor(options: PaymentMethodUnsupportedError.Options = {}) {
252
+ const { method } = options
253
+ super(
254
+ method ? `Payment method "${method}" is not supported.` : 'Payment method is not supported.',
255
+ )
256
+ }
257
+ }
258
+
259
+ export declare namespace PaymentMethodUnsupportedError {
260
+ type Options = {
261
+ /** The unsupported method identifier. */
262
+ method?: string
263
+ }
264
+ }
265
+
208
266
  /**
209
267
  * Insufficient balance in the payment channel.
210
268
  */
211
269
  export class InsufficientBalanceError extends PaymentError {
212
270
  override readonly name = 'InsufficientBalanceError'
271
+ readonly title = 'Insufficient Balance'
213
272
  override readonly status = 402
214
273
  readonly type = 'https://paymentauth.org/problems/stream/insufficient-balance'
215
274
 
@@ -231,6 +290,7 @@ export declare namespace InsufficientBalanceError {
231
290
  */
232
291
  export class InvalidSignatureError extends PaymentError {
233
292
  override readonly name = 'InvalidSignatureError'
293
+ readonly title = 'Invalid Signature'
234
294
  override readonly status = 402
235
295
  readonly type = 'https://paymentauth.org/problems/stream/invalid-signature'
236
296
 
@@ -251,6 +311,7 @@ export declare namespace InvalidSignatureError {
251
311
  */
252
312
  export class SignerMismatchError extends PaymentError {
253
313
  override readonly name = 'SignerMismatchError'
314
+ readonly title = 'Signer Mismatch'
254
315
  override readonly status = 402
255
316
  readonly type = 'https://paymentauth.org/problems/stream/signer-mismatch'
256
317
 
@@ -271,6 +332,7 @@ export declare namespace SignerMismatchError {
271
332
  */
272
333
  export class AmountExceedsDepositError extends PaymentError {
273
334
  override readonly name = 'AmountExceedsDepositError'
335
+ readonly title = 'Amount Exceeds Deposit'
274
336
  override readonly status = 402
275
337
  readonly type = 'https://paymentauth.org/problems/stream/amount-exceeds-deposit'
276
338
 
@@ -291,6 +353,7 @@ export declare namespace AmountExceedsDepositError {
291
353
  */
292
354
  export class DeltaTooSmallError extends PaymentError {
293
355
  override readonly name = 'DeltaTooSmallError'
356
+ readonly title = 'Delta Too Small'
294
357
  override readonly status = 402
295
358
  readonly type = 'https://paymentauth.org/problems/stream/delta-too-small'
296
359
 
@@ -311,6 +374,7 @@ export declare namespace DeltaTooSmallError {
311
374
  */
312
375
  export class ChannelNotFoundError extends PaymentError {
313
376
  override readonly name = 'ChannelNotFoundError'
377
+ readonly title = 'Channel Not Found'
314
378
  override readonly status = 410
315
379
  readonly type = 'https://paymentauth.org/problems/stream/channel-not-found'
316
380
 
@@ -331,6 +395,7 @@ export declare namespace ChannelNotFoundError {
331
395
  */
332
396
  export class ChannelClosedError extends PaymentError {
333
397
  override readonly name = 'ChannelClosedError'
398
+ readonly title = 'Channel Closed'
334
399
  override readonly status = 410
335
400
  readonly type = 'https://paymentauth.org/problems/stream/channel-finalized'
336
401
 
@@ -0,0 +1,76 @@
1
+ import { Method, z } from 'mppx'
2
+ import { describe, expect, expectTypeOf, test } from 'vitest'
3
+
4
+ describe('from', () => {
5
+ test('behavior: creates intent', () => {
6
+ const method = Method.from({
7
+ name: 'tempo',
8
+ intent: 'charge',
9
+ schema: {
10
+ credential: {
11
+ payload: z.object({
12
+ signature: z.string(),
13
+ }),
14
+ },
15
+ request: z.object({
16
+ amount: z.string(),
17
+ currency: z.string(),
18
+ }),
19
+ },
20
+ })
21
+
22
+ expect(method.intent).toBe('charge')
23
+ expect(method.name).toBe('tempo')
24
+ expect(method.schema.request).toBeDefined()
25
+ expect(method.schema.credential.payload).toBeDefined()
26
+ })
27
+
28
+ test('types: intent literal is inferred', () => {
29
+ const method = Method.from({
30
+ name: 'tempo',
31
+ intent: 'charge',
32
+ schema: {
33
+ credential: { payload: z.object({ sig: z.string() }) },
34
+ request: z.object({ amount: z.string() }),
35
+ },
36
+ })
37
+
38
+ expectTypeOf(method.intent).toEqualTypeOf<'charge'>()
39
+ })
40
+
41
+ test('types: name literal is inferred', () => {
42
+ const method = Method.from({
43
+ name: 'tempo',
44
+ intent: 'charge',
45
+ schema: {
46
+ credential: { payload: z.object({ sig: z.string() }) },
47
+ request: z.object({ amount: z.string() }),
48
+ },
49
+ })
50
+
51
+ expectTypeOf(method.name).toEqualTypeOf<'tempo'>()
52
+ })
53
+
54
+ test('types: schema types are preserved', () => {
55
+ const requestSchema = z.object({
56
+ amount: z.string(),
57
+ currency: z.string(),
58
+ })
59
+ const payloadSchema = z.object({
60
+ signature: z.string(),
61
+ type: z.literal('transaction'),
62
+ })
63
+
64
+ const method = Method.from({
65
+ name: 'tempo',
66
+ intent: 'charge',
67
+ schema: {
68
+ credential: { payload: payloadSchema },
69
+ request: requestSchema,
70
+ },
71
+ })
72
+
73
+ expectTypeOf(method.schema.request).toEqualTypeOf(requestSchema)
74
+ expectTypeOf(method.schema.credential.payload).toEqualTypeOf(payloadSchema)
75
+ })
76
+ })
package/src/Method.ts ADDED
@@ -0,0 +1,228 @@
1
+ import type * as Challenge from './Challenge.js'
2
+ import type * as Credential from './Credential.js'
3
+ import type { ExactPartial, LooseOmit, MaybePromise } from './internal/types.js'
4
+ import type * as Receipt from './Receipt.js'
5
+ import type * as Transport from './server/Transport.js'
6
+ import type * as z from './zod.js'
7
+
8
+ /**
9
+ * A payment method.
10
+ */
11
+ export type Method = {
12
+ name: string
13
+ intent: string
14
+ schema: {
15
+ credential: {
16
+ payload: z.ZodMiniType
17
+ }
18
+ request: z.ZodMiniType<Record<string, unknown>>
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Creates a payment method.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { z } from 'zod/mini'
28
+ * import { Method } from 'mppx'
29
+ *
30
+ * const tempoCharge = Method.from({
31
+ * name: 'tempo',
32
+ * intent: 'charge',
33
+ * schema: {
34
+ * credential: {
35
+ * payload: z.object({
36
+ * signature: z.string(),
37
+ * type: z.literal('transaction'),
38
+ * }),
39
+ * },
40
+ * request: z.object({
41
+ * amount: z.string(),
42
+ * currency: z.string(),
43
+ * recipient: z.string(),
44
+ * }),
45
+ * },
46
+ * })
47
+ * ```
48
+ */
49
+ export function from<const method extends Method>(method: method): method {
50
+ return method
51
+ }
52
+
53
+ /**
54
+ * A client-side configured method with credential creation logic.
55
+ */
56
+ export type Client<
57
+ method extends Method = Method,
58
+ context extends z.ZodMiniType | undefined = z.ZodMiniType | undefined,
59
+ > = method & {
60
+ context?: context
61
+ createCredential: CreateCredentialFn<
62
+ method,
63
+ context extends z.ZodMiniType ? z.output<context> : Record<never, never>
64
+ >
65
+ }
66
+ export type AnyClient = Client<any, any>
67
+
68
+ /**
69
+ * A server-side configured method with verification logic.
70
+ */
71
+ export type Server<
72
+ method extends Method = Method,
73
+ defaults extends ExactPartial<z.input<method['schema']['request']>> = {},
74
+ transportOverride = undefined,
75
+ > = method & {
76
+ defaults?: defaults | undefined
77
+ request?: RequestFn<method> | undefined
78
+ respond?: RespondFn<method> | undefined
79
+ transport?: transportOverride | undefined
80
+ verify: VerifyFn<method>
81
+ }
82
+ export type AnyServer = Server<any, any, any>
83
+
84
+ /** Credential creation function for a single method. */
85
+ export type CreateCredentialFn<method extends Method, context = unknown> = (
86
+ parameters: {
87
+ challenge: Challenge.Challenge<
88
+ z.output<method['schema']['request']>,
89
+ method['intent'],
90
+ method['name']
91
+ >
92
+ } & ([keyof context] extends [never] ? unknown : { context: context }),
93
+ ) => Promise<string>
94
+
95
+ /** Request transform function for a single method. */
96
+ export type RequestFn<method extends Method> = (options: {
97
+ credential?: Credential.Credential | null | undefined
98
+ request: z.input<method['schema']['request']>
99
+ }) => MaybePromise<z.input<method['schema']['request']>>
100
+
101
+ /** Verification function for a single method. */
102
+ export type VerifyFn<method extends Method> = (parameters: {
103
+ credential: Credential.Credential<
104
+ z.output<method['schema']['credential']['payload']>,
105
+ Challenge.Challenge<z.output<method['schema']['request']>, method['intent'], method['name']>
106
+ >
107
+ request: z.input<method['schema']['request']>
108
+ }) => Promise<Receipt.Receipt>
109
+
110
+ /**
111
+ * Optional respond function for a server-side method.
112
+ *
113
+ * Called after `verify` succeeds. If it returns a `Response`, the library
114
+ * treats the request as fully handled (e.g. channel open/close) and
115
+ * `withReceipt()` will short-circuit — returning the management response
116
+ * with the receipt header attached without invoking any user-supplied
117
+ * response or generator. If it returns `undefined`, the server handler
118
+ * is expected to serve content via `withReceipt(response)`.
119
+ *
120
+ * **HTTP-only.** The `input` parameter is a `Request` object; MCP transports
121
+ * do not invoke this hook.
122
+ */
123
+ export type RespondFn<method extends Method> = (parameters: {
124
+ credential: Credential.Credential<
125
+ z.output<method['schema']['credential']['payload']>,
126
+ Challenge.Challenge<z.output<method['schema']['request']>, method['intent'], method['name']>
127
+ >
128
+ input: globalThis.Request
129
+ receipt: Receipt.Receipt
130
+ request: z.input<method['schema']['request']>
131
+ }) => MaybePromise<globalThis.Response | undefined>
132
+
133
+ /** Partial request type for defaults. */
134
+ export type RequestDefaults<method extends Method> = ExactPartial<
135
+ z.input<method['schema']['request']>
136
+ >
137
+
138
+ /** Makes fields optional if they exist in defaults. */
139
+ export type WithDefaults<request, defaults> = [keyof defaults] extends [never]
140
+ ? request
141
+ : LooseOmit<request, keyof defaults & string> &
142
+ ExactPartial<Pick<request, keyof defaults & keyof request>>
143
+
144
+ /**
145
+ * Extends a method with client-side credential creation logic.
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * import { Method } from 'mppx'
150
+ * import { Methods } from 'mppx/tempo'
151
+ *
152
+ * const tempoCharge = Method.toClient(Methods.charge, {
153
+ * async createCredential({ challenge }) {
154
+ * return Credential.serialize({ challenge, payload: { ... } })
155
+ * },
156
+ * })
157
+ * ```
158
+ */
159
+ export function toClient<
160
+ const method extends Method,
161
+ const context extends z.ZodMiniType | undefined = undefined,
162
+ >(method: method, options: toClient.Options<method, context>): Client<method, context> {
163
+ const { context, createCredential } = options
164
+ return {
165
+ ...method,
166
+ context,
167
+ createCredential,
168
+ } as Client<method, context>
169
+ }
170
+
171
+ export declare namespace toClient {
172
+ type Options<method extends Method, context extends z.ZodMiniType | undefined = undefined> = {
173
+ context?: context
174
+ createCredential: CreateCredentialFn<
175
+ method,
176
+ context extends z.ZodMiniType ? z.output<context> : Record<never, never>
177
+ >
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Extends a method with server-side verification logic.
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * import { Method } from 'mppx'
187
+ * import { Methods } from 'mppx/tempo'
188
+ *
189
+ * const tempoCharge = Method.toServer(Methods.charge, {
190
+ * async verify({ credential }) {
191
+ * // verification logic
192
+ * return { status: 'success', ... }
193
+ * },
194
+ * })
195
+ * ```
196
+ */
197
+ export function toServer<
198
+ const method extends Method,
199
+ const defaults extends RequestDefaults<method> = {},
200
+ const transportOverride extends Transport.AnyTransport | undefined = undefined,
201
+ >(
202
+ method: method,
203
+ options: toServer.Options<method, defaults, transportOverride>,
204
+ ): Server<method, defaults, transportOverride> {
205
+ const { defaults, request, respond, transport, verify } = options
206
+ return {
207
+ ...method,
208
+ defaults,
209
+ request,
210
+ respond,
211
+ transport,
212
+ verify,
213
+ } as Server<method, defaults, transportOverride>
214
+ }
215
+
216
+ export declare namespace toServer {
217
+ type Options<
218
+ method extends Method,
219
+ defaults extends RequestDefaults<method> = {},
220
+ transportOverride extends Transport.AnyTransport | undefined = undefined,
221
+ > = {
222
+ defaults?: defaults | undefined
223
+ request?: RequestFn<method> | undefined
224
+ respond?: RespondFn<method> | undefined
225
+ transport?: transportOverride | undefined
226
+ verify: VerifyFn<method>
227
+ }
228
+ }
@@ -1,5 +1,5 @@
1
1
  import { PaymentRequest } from 'mppx'
2
- import { MethodIntents as Intents } from 'mppx/tempo'
2
+ import { Methods } from 'mppx/tempo'
3
3
  import { describe, expect, test } from 'vitest'
4
4
 
5
5
  describe('from', () => {
@@ -21,7 +21,7 @@ describe('from', () => {
21
21
 
22
22
  describe('fromIntent', () => {
23
23
  test('creates a validated request from intent', () => {
24
- const request = PaymentRequest.fromIntent(Intents.charge, {
24
+ const request = PaymentRequest.fromIntent(Methods.charge, {
25
25
  amount: '1',
26
26
  currency: '0x20c0000000000000000000000000000000000001',
27
27
  decimals: 6,
@@ -39,7 +39,7 @@ describe('fromIntent', () => {
39
39
  })
40
40
 
41
41
  test('includes methodDetails fields', () => {
42
- const request = PaymentRequest.fromIntent(Intents.charge, {
42
+ const request = PaymentRequest.fromIntent(Methods.charge, {
43
43
  amount: '1',
44
44
  currency: '0x20c0000000000000000000000000000000000001',
45
45
  decimals: 6,
@@ -62,7 +62,7 @@ describe('fromIntent', () => {
62
62
 
63
63
  test('throws on invalid request', () => {
64
64
  expect(() =>
65
- PaymentRequest.fromIntent(Intents.charge, {
65
+ PaymentRequest.fromIntent(Methods.charge, {
66
66
  amount: 123,
67
67
  currency: '0x20c0000000000000000000000000000000000001',
68
68
  recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00',
@@ -1,6 +1,6 @@
1
1
  import { Base64 } from 'ox'
2
2
  import type { Compute } from './internal/types.js'
3
- import type * as MethodIntent from './MethodIntent.js'
3
+ import type * as Method from './Method.js'
4
4
  import type * as z from './zod.js'
5
5
 
6
6
  /**
@@ -62,16 +62,16 @@ export function from<const request extends Request>(request: request): request {
62
62
  /**
63
63
  * Creates a validated request from a method intent.
64
64
  *
65
- * @param intent - The method intent to validate against.
65
+ * @param method - The method to validate against.
66
66
  * @param request - Request parameters.
67
67
  * @returns A validated request.
68
68
  *
69
69
  * @example
70
70
  * ```ts
71
71
  * import { Request } from 'mppx'
72
- * import { Intents } from 'mppx/tempo'
72
+ * import { Methods } from 'mppx/tempo'
73
73
  *
74
- * const request = Request.fromIntent(Intents.charge, {
74
+ * const request = Request.fromIntent(Methods.charge, {
75
75
  * amount: '1000000',
76
76
  * currency: '0x20c0000000000000000000000000000000000001',
77
77
  * recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00',
@@ -80,11 +80,11 @@ export function from<const request extends Request>(request: request): request {
80
80
  * })
81
81
  * ```
82
82
  */
83
- export function fromIntent<const intent extends MethodIntent.MethodIntent>(
84
- intent: intent,
85
- request: z.input<intent['schema']['request']>,
86
- ): Request<z.output<intent['schema']['request']>> {
87
- return intent.schema.request.parse(request) as Request<z.output<intent['schema']['request']>>
83
+ export function fromIntent<const method extends Method.Method>(
84
+ method: method,
85
+ request: z.input<method['schema']['request']>,
86
+ ): Request<z.output<method['schema']['request']>> {
87
+ return method.schema.request.parse(request) as Request<z.output<method['schema']['request']>>
88
88
  }
89
89
 
90
90
  /**
package/src/cli.test.ts CHANGED
@@ -11,7 +11,7 @@ import { accounts, asset, client, fundAccount } from '~test/tempo/viem.js'
11
11
  import * as Store from './Store.js'
12
12
  import * as Mppx_server from './server/Mppx.js'
13
13
  import { toNodeListener } from './server/Mppx.js'
14
- import { tempo } from './tempo/server/MethodIntents.js'
14
+ import { tempo } from './tempo/server/Methods.js'
15
15
 
16
16
  const cliPath = path.resolve(import.meta.dirname, 'cli.ts')
17
17
  const cwd = path.resolve(import.meta.dirname, '..')
@@ -117,7 +117,7 @@ describe('basic charge (examples/basic)', () => {
117
117
  })
118
118
 
119
119
  try {
120
- const { stdout } = await runAsync([httpServer.url, '--rpc-url', rpcUrl, '--yes', '-s'], {
120
+ const { stdout } = await runAsync([httpServer.url, '--rpc-url', rpcUrl, '-s'], {
121
121
  input: '',
122
122
  })
123
123
  expect(stdout).toContain('paid')
@@ -179,7 +179,7 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
179
179
 
180
180
  try {
181
181
  const { stdout } = await runAsync(
182
- [httpServer.url, '--rpc-url', rpcUrl, '--yes', '-s', '--deposit', '10'],
182
+ [httpServer.url, '--rpc-url', rpcUrl, '-s', '--deposit', '10'],
183
183
  { input: '' },
184
184
  )
185
185
  expect(stdout).toContain('scraped-content')
@@ -227,9 +227,10 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
227
227
 
228
228
  try {
229
229
  // First request: open a channel, answer "y" to proceed, "n" to close channel
230
- const first = await runAsync([httpServer.url, '--rpc-url', rpcUrl, '--deposit', '10'], {
231
- input: 'y\nn\n',
232
- })
230
+ const first = await runAsync(
231
+ [httpServer.url, '--rpc-url', rpcUrl, '--confirm', '--deposit', '10'],
232
+ { input: 'y\nn\n' },
233
+ )
233
234
  expect(first.stdout).toContain('scraped-content')
234
235
 
235
236
  // Extract channel ID from stderr (logged as "Channel opened 0x...")
@@ -239,17 +240,7 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
239
240
 
240
241
  // Second request: reuse the channel via --channel
241
242
  const second = await runAsync(
242
- [
243
- httpServer.url,
244
- '--rpc-url',
245
- rpcUrl,
246
- '--yes',
247
- '-s',
248
- '--channel',
249
- channelId,
250
- '--deposit',
251
- '10',
252
- ],
243
+ [httpServer.url, '--rpc-url', rpcUrl, '-s', '--channel', channelId, '--deposit', '10'],
253
244
  { input: '' },
254
245
  )
255
246
  expect(second.stdout).toContain('scraped-content')
@@ -322,10 +313,9 @@ describe('session sse (examples/session/sse)', () => {
322
313
  })
323
314
 
324
315
  try {
325
- const { stdout } = await runAsync(
326
- [httpServer.url, '--rpc-url', rpcUrl, '--yes', '--deposit', '10'],
327
- { input: '' },
328
- )
316
+ const { stdout } = await runAsync([httpServer.url, '--rpc-url', rpcUrl, '--deposit', '10'], {
317
+ input: '',
318
+ })
329
319
  expect(stdout.trim()).toBe('Hello world!')
330
320
  } finally {
331
321
  httpServer.close()
@@ -528,9 +518,9 @@ test('mppx --help', () => {
528
518
  -L, --location Follow redirects
529
519
  -X, --method <method> HTTP method
530
520
  --channel <id> Reuse existing stream channel ID
521
+ --confirm Show confirmation prompts
531
522
  --deposit <amount> Deposit amount for stream payments (human-readable units)
532
523
  --json <json> Send JSON body (sets Content-Type and Accept, implies POST)
533
- --yes Skip confirmation prompts
534
524
  -V, --version Display version number
535
525
  -h, --help Display this message
536
526