mppx 0.1.0 → 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 (231) hide show
  1. package/README.md +1 -1
  2. package/dist/Challenge.d.ts +18 -18
  3. package/dist/Challenge.d.ts.map +1 -1
  4. package/dist/Challenge.js +8 -8
  5. package/dist/Challenge.js.map +1 -1
  6. package/dist/Errors.d.ts +58 -8
  7. package/dist/Errors.d.ts.map +1 -1
  8. package/dist/Errors.js +51 -9
  9. package/dist/Errors.js.map +1 -1
  10. package/dist/Method.d.ts +154 -0
  11. package/dist/Method.d.ts.map +1 -0
  12. package/dist/Method.js +81 -0
  13. package/dist/Method.js.map +1 -0
  14. package/dist/PaymentRequest.d.ts +5 -5
  15. package/dist/PaymentRequest.d.ts.map +1 -1
  16. package/dist/PaymentRequest.js +5 -5
  17. package/dist/cli.js +67 -18
  18. package/dist/cli.js.map +1 -1
  19. package/dist/client/Methods.d.ts +2 -2
  20. package/dist/client/Methods.d.ts.map +1 -1
  21. package/dist/client/Methods.js +2 -2
  22. package/dist/client/Methods.js.map +1 -1
  23. package/dist/client/Mppx.d.ts +12 -7
  24. package/dist/client/Mppx.d.ts.map +1 -1
  25. package/dist/client/Mppx.js +10 -5
  26. package/dist/client/Mppx.js.map +1 -1
  27. package/dist/client/internal/Fetch.d.ts +13 -11
  28. package/dist/client/internal/Fetch.d.ts.map +1 -1
  29. package/dist/client/internal/Fetch.js +8 -4
  30. package/dist/client/internal/Fetch.js.map +1 -1
  31. package/dist/index.d.ts +1 -2
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +1 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/mcp-sdk/client/McpClient.d.ts +6 -6
  36. package/dist/mcp-sdk/client/McpClient.d.ts.map +1 -1
  37. package/dist/mcp-sdk/client/McpClient.js +4 -4
  38. package/dist/mcp-sdk/client/McpClient.js.map +1 -1
  39. package/dist/middlewares/elysia.d.ts +1 -1
  40. package/dist/middlewares/express.d.ts +1 -1
  41. package/dist/middlewares/hono.d.ts +1 -1
  42. package/dist/middlewares/internal/mppx.d.ts +7 -7
  43. package/dist/middlewares/internal/mppx.d.ts.map +1 -1
  44. package/dist/middlewares/internal/mppx.js +5 -5
  45. package/dist/middlewares/internal/mppx.js.map +1 -1
  46. package/dist/middlewares/nextjs.d.ts +1 -1
  47. package/dist/proxy/Service.js +2 -2
  48. package/dist/proxy/Service.js.map +1 -1
  49. package/dist/server/Methods.d.ts +2 -2
  50. package/dist/server/Methods.d.ts.map +1 -1
  51. package/dist/server/Methods.js +2 -2
  52. package/dist/server/Methods.js.map +1 -1
  53. package/dist/server/Mppx.d.ts +17 -17
  54. package/dist/server/Mppx.d.ts.map +1 -1
  55. package/dist/server/Mppx.js +9 -9
  56. package/dist/server/Mppx.js.map +1 -1
  57. package/dist/stripe/{Intents.d.ts → Methods.d.ts} +22 -22
  58. package/dist/stripe/Methods.d.ts.map +1 -0
  59. package/dist/stripe/Methods.js +42 -0
  60. package/dist/stripe/Methods.js.map +1 -0
  61. package/dist/stripe/client/Charge.d.ts +48 -44
  62. package/dist/stripe/client/Charge.d.ts.map +1 -1
  63. package/dist/stripe/client/Charge.js +22 -17
  64. package/dist/stripe/client/Charge.js.map +1 -1
  65. package/dist/stripe/client/{MethodIntents.d.ts → Methods.d.ts} +25 -24
  66. package/dist/stripe/client/Methods.d.ts.map +1 -0
  67. package/dist/stripe/client/{MethodIntents.js → Methods.js} +4 -4
  68. package/dist/stripe/client/Methods.js.map +1 -0
  69. package/dist/stripe/client/index.d.ts +1 -1
  70. package/dist/stripe/client/index.d.ts.map +1 -1
  71. package/dist/stripe/client/index.js +1 -1
  72. package/dist/stripe/client/index.js.map +1 -1
  73. package/dist/stripe/index.d.ts +1 -1
  74. package/dist/stripe/index.d.ts.map +1 -1
  75. package/dist/stripe/index.js +1 -1
  76. package/dist/stripe/index.js.map +1 -1
  77. package/dist/stripe/internal/types.d.ts +25 -0
  78. package/dist/stripe/internal/types.d.ts.map +1 -0
  79. package/dist/stripe/internal/types.js +2 -0
  80. package/dist/stripe/internal/types.js.map +1 -0
  81. package/dist/stripe/server/Charge.d.ts +47 -28
  82. package/dist/stripe/server/Charge.d.ts.map +1 -1
  83. package/dist/stripe/server/Charge.js +90 -32
  84. package/dist/stripe/server/Charge.js.map +1 -1
  85. package/dist/stripe/server/{MethodIntents.d.ts → Methods.d.ts} +24 -23
  86. package/dist/stripe/server/Methods.d.ts.map +1 -0
  87. package/dist/stripe/server/{MethodIntents.js → Methods.js} +3 -3
  88. package/dist/stripe/server/Methods.js.map +1 -0
  89. package/dist/stripe/server/index.d.ts +1 -1
  90. package/dist/stripe/server/index.d.ts.map +1 -1
  91. package/dist/stripe/server/index.js +1 -1
  92. package/dist/stripe/server/index.js.map +1 -1
  93. package/dist/tempo/{Intents.d.ts → Methods.d.ts} +72 -69
  94. package/dist/tempo/Methods.d.ts.map +1 -0
  95. package/dist/tempo/Methods.js +118 -0
  96. package/dist/tempo/Methods.js.map +1 -0
  97. package/dist/tempo/client/ChannelOps.d.ts +1 -1
  98. package/dist/tempo/client/ChannelOps.js +1 -1
  99. package/dist/tempo/client/Charge.d.ts +25 -25
  100. package/dist/tempo/client/Charge.d.ts.map +1 -1
  101. package/dist/tempo/client/Charge.js +3 -3
  102. package/dist/tempo/client/Charge.js.map +1 -1
  103. package/dist/tempo/client/{MethodIntents.d.ts → Methods.d.ts} +74 -70
  104. package/dist/tempo/client/Methods.d.ts.map +1 -0
  105. package/dist/tempo/client/{MethodIntents.js → Methods.js} +3 -3
  106. package/dist/tempo/client/Methods.js.map +1 -0
  107. package/dist/tempo/client/Session.d.ts +49 -45
  108. package/dist/tempo/client/Session.d.ts.map +1 -1
  109. package/dist/tempo/client/Session.js +4 -4
  110. package/dist/tempo/client/Session.js.map +1 -1
  111. package/dist/tempo/client/SessionManager.d.ts +1 -1
  112. package/dist/tempo/client/SessionManager.d.ts.map +1 -1
  113. package/dist/tempo/client/SessionManager.js +10 -5
  114. package/dist/tempo/client/SessionManager.js.map +1 -1
  115. package/dist/tempo/client/index.d.ts +1 -1
  116. package/dist/tempo/client/index.d.ts.map +1 -1
  117. package/dist/tempo/client/index.js +1 -1
  118. package/dist/tempo/client/index.js.map +1 -1
  119. package/dist/tempo/index.d.ts +1 -1
  120. package/dist/tempo/index.d.ts.map +1 -1
  121. package/dist/tempo/index.js +1 -1
  122. package/dist/tempo/index.js.map +1 -1
  123. package/dist/tempo/internal/defaults.d.ts +1 -1
  124. package/dist/tempo/internal/defaults.js +1 -1
  125. package/dist/tempo/server/Charge.d.ts +27 -27
  126. package/dist/tempo/server/Charge.d.ts.map +1 -1
  127. package/dist/tempo/server/Charge.js +3 -3
  128. package/dist/tempo/server/Charge.js.map +1 -1
  129. package/dist/tempo/server/{MethodIntents.d.ts → Methods.d.ts} +73 -69
  130. package/dist/tempo/server/Methods.d.ts.map +1 -0
  131. package/dist/tempo/server/{MethodIntents.js → Methods.js} +4 -4
  132. package/dist/tempo/server/Methods.js.map +1 -0
  133. package/dist/tempo/server/Session.d.ts +51 -47
  134. package/dist/tempo/server/Session.d.ts.map +1 -1
  135. package/dist/tempo/server/Session.js +4 -4
  136. package/dist/tempo/server/Session.js.map +1 -1
  137. package/dist/tempo/server/index.d.ts +6 -0
  138. package/dist/tempo/server/index.d.ts.map +1 -0
  139. package/dist/tempo/server/index.js +6 -0
  140. package/dist/tempo/server/index.js.map +1 -0
  141. package/dist/tempo/server/internal/transport.d.ts.map +1 -1
  142. package/dist/tempo/server/internal/transport.js +2 -1
  143. package/dist/tempo/server/internal/transport.js.map +1 -1
  144. package/package.json +1 -1
  145. package/src/Challenge.test-d.ts +3 -3
  146. package/src/Challenge.test.ts +6 -6
  147. package/src/Challenge.ts +34 -34
  148. package/src/Errors.test.ts +75 -21
  149. package/src/Errors.ts +74 -9
  150. package/src/Method.test.ts +76 -0
  151. package/src/Method.ts +228 -0
  152. package/src/PaymentRequest.test.ts +4 -4
  153. package/src/PaymentRequest.ts +9 -9
  154. package/src/cli.test.ts +12 -22
  155. package/src/cli.ts +74 -21
  156. package/src/client/Methods.ts +2 -2
  157. package/src/client/Mppx.test-d.ts +6 -6
  158. package/src/client/Mppx.test.ts +26 -22
  159. package/src/client/Mppx.ts +29 -13
  160. package/src/client/Transport.test.ts +2 -2
  161. package/src/client/internal/Fetch.test.ts +35 -1
  162. package/src/client/internal/Fetch.ts +36 -27
  163. package/src/index.ts +1 -2
  164. package/src/mcp-sdk/client/McpClient.ts +11 -13
  165. package/src/middlewares/elysia.ts +1 -1
  166. package/src/middlewares/express.ts +1 -1
  167. package/src/middlewares/hono.ts +1 -1
  168. package/src/middlewares/internal/mppx.ts +10 -10
  169. package/src/middlewares/nextjs.ts +1 -1
  170. package/src/proxy/Service.ts +2 -2
  171. package/src/server/Methods.ts +2 -2
  172. package/src/server/Mppx.test-d.ts +27 -29
  173. package/src/server/Mppx.test.ts +23 -19
  174. package/src/server/Mppx.ts +43 -43
  175. package/src/server/Transport.test.ts +3 -3
  176. package/src/stripe/Charge.integration.test.ts +4 -1
  177. package/src/stripe/{Intents.test.ts → Methods.test.ts} +12 -12
  178. package/src/stripe/Methods.ts +45 -0
  179. package/src/stripe/client/Charge.test.ts +189 -0
  180. package/src/stripe/client/Charge.ts +40 -31
  181. package/src/stripe/client/{MethodIntents.ts → Methods.ts} +3 -3
  182. package/src/stripe/client/index.ts +1 -1
  183. package/src/stripe/index.ts +1 -1
  184. package/src/stripe/internal/types.ts +22 -0
  185. package/src/stripe/server/Charge.test.ts +241 -0
  186. package/src/stripe/server/Charge.ts +124 -38
  187. package/src/stripe/server/{MethodIntents.ts → Methods.ts} +2 -2
  188. package/src/stripe/server/index.ts +1 -1
  189. package/src/tempo/{Intents.test.ts → Methods.test.ts} +15 -15
  190. package/src/tempo/{Intents.ts → Methods.ts} +77 -22
  191. package/src/tempo/client/ChannelOps.ts +1 -1
  192. package/src/tempo/client/Charge.ts +3 -3
  193. package/src/tempo/client/{MethodIntents.ts → Methods.ts} +2 -2
  194. package/src/tempo/client/Session.ts +4 -4
  195. package/src/tempo/client/SessionManager.ts +11 -5
  196. package/src/tempo/client/index.ts +1 -1
  197. package/src/tempo/index.ts +1 -1
  198. package/src/tempo/internal/defaults.ts +1 -1
  199. package/src/tempo/server/Charge.ts +4 -7
  200. package/src/tempo/server/{MethodIntents.ts → Methods.ts} +3 -3
  201. package/src/tempo/server/Session.test.ts +4 -7
  202. package/src/tempo/server/Session.ts +6 -6
  203. package/src/tempo/server/index.ts +1 -1
  204. package/src/tempo/server/internal/transport.ts +3 -2
  205. package/dist/Intent.d.ts +0 -101
  206. package/dist/Intent.d.ts.map +0 -1
  207. package/dist/Intent.js +0 -83
  208. package/dist/Intent.js.map +0 -1
  209. package/dist/MethodIntent.d.ts +0 -225
  210. package/dist/MethodIntent.d.ts.map +0 -1
  211. package/dist/MethodIntent.js +0 -156
  212. package/dist/MethodIntent.js.map +0 -1
  213. package/dist/stripe/Intents.d.ts.map +0 -1
  214. package/dist/stripe/Intents.js +0 -27
  215. package/dist/stripe/Intents.js.map +0 -1
  216. package/dist/stripe/client/MethodIntents.d.ts.map +0 -1
  217. package/dist/stripe/client/MethodIntents.js.map +0 -1
  218. package/dist/stripe/server/MethodIntents.d.ts.map +0 -1
  219. package/dist/stripe/server/MethodIntents.js.map +0 -1
  220. package/dist/tempo/Intents.d.ts.map +0 -1
  221. package/dist/tempo/Intents.js +0 -81
  222. package/dist/tempo/Intents.js.map +0 -1
  223. package/dist/tempo/client/MethodIntents.d.ts.map +0 -1
  224. package/dist/tempo/client/MethodIntents.js.map +0 -1
  225. package/dist/tempo/server/MethodIntents.d.ts.map +0 -1
  226. package/dist/tempo/server/MethodIntents.js.map +0 -1
  227. package/src/Intent.test.ts +0 -180
  228. package/src/Intent.ts +0 -109
  229. package/src/MethodIntent.test.ts +0 -303
  230. package/src/MethodIntent.ts +0 -388
  231. package/src/stripe/Intents.ts +0 -27
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
 
package/src/cli.ts CHANGED
@@ -42,9 +42,9 @@ cli
42
42
  .option('-L, --location', 'Follow redirects')
43
43
  .option('-X, --method <method>', 'HTTP method')
44
44
  .option('--channel <id>', 'Reuse existing stream channel ID')
45
+ .option('--confirm', 'Show confirmation prompts')
45
46
  .option('--deposit <amount>', 'Deposit amount for stream payments (human-readable units)')
46
47
  .option('--json <json>', 'Send JSON body (sets Content-Type and Accept, implies POST)')
47
- .option('--yes', 'Skip confirmation prompts')
48
48
  .example(`${name} example.com/content`)
49
49
  .example(`${name} example.com/api --json '{"key":"value"}'`)
50
50
  .action(async (rawUrl: string | undefined, rawOptions: unknown) => {
@@ -52,6 +52,7 @@ cli
52
52
  z.object({
53
53
  account: z.optional(z.string()),
54
54
  channel: z.optional(z.coerce.string()),
55
+ confirm: z.optional(z.boolean()),
55
56
  data: z.optional(z.string()),
56
57
  deposit: z.optional(z.union([z.string(), z.number()])),
57
58
  fail: z.optional(z.boolean()),
@@ -65,7 +66,6 @@ cli
65
66
  silent: z.optional(z.boolean()),
66
67
  userAgent: z.optional(z.string()),
67
68
  verbose: z.optional(z.boolean()),
68
- yes: z.optional(z.boolean()),
69
69
  }),
70
70
  rawOptions,
71
71
  )
@@ -76,7 +76,7 @@ cli
76
76
 
77
77
  const silent = options.silent ?? false
78
78
  const info = silent ? (_msg: string) => {} : (msg: string) => process.stderr.write(msg)
79
- if (silent) options.yes = true
79
+ if (silent) options.confirm = false
80
80
 
81
81
  const accountName = resolveAccountName(options.account)
82
82
  const privateKey = process.env.MPPX_PRIVATE_KEY ?? (await createKeychain(accountName).get())
@@ -266,10 +266,9 @@ cli
266
266
  for (const line of rest) info(`${indent}${line}\n`)
267
267
  }
268
268
  }
269
- info('\n')
270
-
271
- if (!options.yes) {
272
- const ok = await confirm(`Proceed with ${challenge.intent}?`)
269
+ if (options.confirm) {
270
+ info('\n')
271
+ const ok = await confirm(`Proceed with ${challenge.intent}?`, true)
273
272
  if (!ok) {
274
273
  info('Aborted.\n')
275
274
  process.exit(0)
@@ -336,7 +335,7 @@ cli
336
335
  const depositDisplay = depositRaw
337
336
  ? ` ${pc.dim(`(deposit ${depositRaw} ${tokenSymbol})`)}`
338
337
  : ''
339
- info(`${pc.dim(`Channel opened ${parsed.payload.channelId}`)}${depositDisplay}\n`)
338
+ info(`\n${pc.dim(`Channel opened ${parsed.payload.channelId}`)}${depositDisplay}\n`)
340
339
  }
341
340
  }
342
341
 
@@ -385,11 +384,25 @@ cli
385
384
  }
386
385
  info(`\n${pc.bold(pc.green('Payment Receipt'))}\n`)
387
386
  const rows: [string, string][] = []
387
+ const channelId = receiptJson.channelId
388
+ const reference = receiptJson.reference
389
+ const skipReference = channelId && reference && channelId === reference
390
+ const receiptBalanceKeys = new Set(['acceptedCumulative', 'spent'])
388
391
  for (const [key, value] of Object.entries(receiptJson)) {
389
392
  if (value === undefined || shownKeys.has(key)) continue
390
- if (key === 'reference' && typeof value === 'string' && explorerUrl)
393
+ if (key === 'reference' && skipReference) continue
394
+ if (receiptBalanceKeys.has(key) && typeof value === 'string') {
395
+ rows.push([
396
+ key,
397
+ `${value} ${pc.dim(`(${fmtBalance(BigInt(value), tokenSymbol, tokenDecimals)})`)}`,
398
+ ])
399
+ } else if (
400
+ (key === 'reference' || key === 'txHash') &&
401
+ typeof value === 'string' &&
402
+ explorerUrl
403
+ ) {
391
404
  rows.push([key, pc.link(`${explorerUrl}/tx/${value}`, value)])
392
- else rows.push([key, String(value)])
405
+ } else rows.push([key, String(value)])
393
406
  }
394
407
  rows.sort(([a], [b]) => a.localeCompare(b))
395
408
  const pad = Math.max(...rows.map(([k]) => k.length))
@@ -426,7 +439,7 @@ cli
426
439
  : 0n
427
440
  let _voucherSeq = 0
428
441
 
429
- const termBg = await detectTerminalBg()
442
+ const termBg = verbose ? await detectTerminalBg() : undefined
430
443
  const chunkBgs = (() => {
431
444
  if (!termBg || !pc.isColorSupported) return undefined
432
445
  const clamp = (n: number) => Math.max(0, Math.min(255, Math.round(n)))
@@ -513,18 +526,26 @@ cli
513
526
  const receipt = JSON.parse(data) as Record<string, unknown>
514
527
  info(`\n\n${pc.bold(pc.green('Payment Receipt'))}\n`)
515
528
  const rows: [string, string][] = []
529
+ const skipRef =
530
+ receipt.channelId &&
531
+ receipt.reference &&
532
+ receipt.channelId === receipt.reference
516
533
  for (const [key, value] of Object.entries(receipt)) {
517
534
  if (value === undefined || shownKeys.has(key)) continue
518
- if (key === 'channelId' && value === receipt.reference) continue
535
+ if (key === 'reference' && skipRef) continue
519
536
  const receiptBalanceKeys = ['acceptedCumulative', 'spent']
520
537
  if (receiptBalanceKeys.includes(key) && typeof value === 'string') {
521
538
  rows.push([
522
539
  key,
523
540
  `${value} ${pc.dim(`(${fmtBalance(BigInt(value), tokenSymbol, tokenDecimals)})`)}`,
524
541
  ])
525
- } else if (key === 'reference' && typeof value === 'string' && explorerUrl)
542
+ } else if (
543
+ (key === 'reference' || key === 'txHash') &&
544
+ typeof value === 'string' &&
545
+ explorerUrl
546
+ ) {
526
547
  rows.push([key, pc.link(`${explorerUrl}/tx/${value}`, value)])
527
- else rows.push([key, String(value)])
548
+ } else rows.push([key, String(value)])
528
549
  }
529
550
  rows.sort(([a], [b]) => a.localeCompare(b))
530
551
  const rpad = Math.max(...rows.map(([k]) => k.length))
@@ -586,8 +607,23 @@ cli
586
607
  headers: { Authorization: closeCred },
587
608
  })
588
609
  if (closeRes.ok) {
610
+ const closeReceiptHeader = closeRes.headers.get('Payment-Receipt')
611
+ let closeTxHash: string | undefined
612
+ if (closeReceiptHeader) {
613
+ try {
614
+ const r = JSON.parse(Base64.toString(closeReceiptHeader)) as Record<
615
+ string,
616
+ unknown
617
+ >
618
+ if (typeof r.txHash === 'string') closeTxHash = r.txHash
619
+ } catch {}
620
+ }
621
+ const txInfo =
622
+ closeTxHash && explorerUrl
623
+ ? ` ${pc.dim(pc.link(`${explorerUrl}/tx/${closeTxHash}`, closeTxHash))}`
624
+ : ''
589
625
  info(
590
- `\n${pc.dim('Channel closed.')} ${pc.dim(`Spent ${fmtBalance(cumulativeAmount, tokenSymbol, tokenDecimals)}.`)}\n`,
626
+ `\n${pc.dim('Channel closed.')} ${pc.dim(`Spent ${fmtBalance(cumulativeAmount, tokenSymbol, tokenDecimals)}.`)}${txInfo}\n`,
591
627
  )
592
628
  } else {
593
629
  info(
@@ -605,10 +641,10 @@ cli
605
641
  streamChannelId &&
606
642
  streamEscrowContract &&
607
643
  streamChainId
608
- if (shouldClose && !options.yes) {
644
+ if (shouldClose && options.confirm) {
609
645
  info('\n')
610
646
  }
611
- if (shouldClose && !(options.yes || (await confirm('Close channel?')))) {
647
+ if (shouldClose && options.confirm && !(await confirm('Close channel?', true))) {
612
648
  info(`${pc.dim('Kept channel open.')}\n`)
613
649
  } else if (shouldClose) {
614
650
  const signature = await signVoucher(
@@ -638,8 +674,23 @@ cli
638
674
  })
639
675
  if (closeRes.ok) {
640
676
  deleteChannelState(streamChannelId!)
677
+ const closeReceiptHeader = closeRes.headers.get('Payment-Receipt')
678
+ let closeTxHash: string | undefined
679
+ if (closeReceiptHeader) {
680
+ try {
681
+ const r = JSON.parse(Base64.toString(closeReceiptHeader)) as Record<
682
+ string,
683
+ unknown
684
+ >
685
+ if (typeof r.txHash === 'string') closeTxHash = r.txHash
686
+ } catch {}
687
+ }
688
+ const txInfo =
689
+ closeTxHash && explorerUrl
690
+ ? ` ${pc.dim(pc.link(`${explorerUrl}/tx/${closeTxHash}`, closeTxHash))}`
691
+ : ''
641
692
  info(
642
- `${pc.dim('Channel closed.')} ${pc.dim(`Spent ${fmtBalance(streamCumulativeAmount, tokenSymbol, tokenDecimals)}.`)}\n`,
693
+ `\n${pc.dim('Channel closed.')} ${pc.dim(`Spent ${fmtBalance(streamCumulativeAmount, tokenSymbol, tokenDecimals)}.`)}${txInfo}\n`,
643
694
  )
644
695
  } else {
645
696
  const closeBody = await closeRes.text().catch(() => '')
@@ -1097,12 +1148,14 @@ function prompt(message: string): Promise<string | undefined> {
1097
1148
  })
1098
1149
  }
1099
1150
 
1100
- function confirm(prompt: string): Promise<boolean> {
1151
+ function confirm(prompt: string, defaultYes = false): Promise<boolean> {
1101
1152
  const reader = readline.createInterface({ input: process.stdin, output: process.stderr })
1102
1153
  return new Promise((resolve) => {
1103
- reader.question(`${pc.bold(`▸ ${prompt}`)} ${pc.dim('(y/N)')} `, (answer) => {
1154
+ const hint = defaultYes ? '(Y/n)' : '(y/N)'
1155
+ reader.question(`${pc.bold(`▸ ${prompt}`)} ${pc.dim(hint)} `, (answer) => {
1104
1156
  reader.close()
1105
- resolve(answer.trim().toLowerCase() === 'y')
1157
+ const trimmed = answer.trim().toLowerCase()
1158
+ resolve(trimmed === '' ? defaultYes : trimmed === 'y')
1106
1159
  })
1107
1160
  })
1108
1161
  }
@@ -1,3 +1,3 @@
1
- export { stripe } from '../stripe/client/MethodIntents.js'
2
- export { tempo } from '../tempo/client/MethodIntents.js'
1
+ export { stripe } from '../stripe/client/index.js'
2
+ export { tempo } from '../tempo/client/index.js'
3
3
  export { session } from '../tempo/client/Session.js'
@@ -1,8 +1,8 @@
1
1
  import type { Account } from 'viem'
2
2
  import { describe, expectTypeOf, test } from 'vitest'
3
- import * as MethodIntent from '../MethodIntent.js'
3
+ import * as Method from '../Method.js'
4
4
  import { charge } from '../tempo/client/Charge.js'
5
- import * as Intents from '../tempo/Intents.js'
5
+ import * as Methods from '../tempo/Methods.js'
6
6
  import * as z from '../zod.js'
7
7
  import * as Mppx from './Mppx.js'
8
8
 
@@ -13,8 +13,8 @@ describe('Mppx', () => {
13
13
  })
14
14
  const mppx = Mppx.create({ methods: [method] })
15
15
 
16
- expectTypeOf(mppx.methods).toMatchTypeOf<readonly MethodIntent.AnyClient[]>()
17
- expectTypeOf(mppx.methods[0]?.name).toEqualTypeOf<'charge'>()
16
+ expectTypeOf(mppx.methods).toMatchTypeOf<readonly Method.AnyClient[]>()
17
+ expectTypeOf(mppx.methods[0]?.intent).toEqualTypeOf<'charge'>()
18
18
  })
19
19
 
20
20
  test('has createCredential function', () => {
@@ -38,7 +38,7 @@ describe('create.Config', () => {
38
38
 
39
39
  describe('Method.toClient', () => {
40
40
  test('createCredential receives typed challenge', () => {
41
- MethodIntent.toClient(Intents.charge, {
41
+ Method.toClient(Methods.charge, {
42
42
  async createCredential({ challenge }) {
43
43
  expectTypeOf(challenge.method).toBeString()
44
44
  expectTypeOf(challenge.intent).toBeString()
@@ -52,7 +52,7 @@ describe('Method.toClient', () => {
52
52
  })
53
53
 
54
54
  test('createCredential receives typed context when provided', () => {
55
- MethodIntent.toClient(Intents.charge, {
55
+ Method.toClient(Methods.charge, {
56
56
  context: z.object({
57
57
  account: z.custom<Account>(),
58
58
  extra: z.optional(z.string()),