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.
- package/README.md +1 -1
- package/dist/Challenge.d.ts +18 -18
- package/dist/Challenge.d.ts.map +1 -1
- package/dist/Challenge.js +8 -8
- package/dist/Challenge.js.map +1 -1
- package/dist/Errors.d.ts +58 -8
- package/dist/Errors.d.ts.map +1 -1
- package/dist/Errors.js +51 -9
- package/dist/Errors.js.map +1 -1
- package/dist/Method.d.ts +154 -0
- package/dist/Method.d.ts.map +1 -0
- package/dist/Method.js +81 -0
- package/dist/Method.js.map +1 -0
- package/dist/PaymentRequest.d.ts +5 -5
- package/dist/PaymentRequest.d.ts.map +1 -1
- package/dist/PaymentRequest.js +5 -5
- package/dist/cli.js +67 -18
- package/dist/cli.js.map +1 -1
- package/dist/client/Methods.d.ts +2 -2
- package/dist/client/Methods.d.ts.map +1 -1
- package/dist/client/Methods.js +2 -2
- package/dist/client/Methods.js.map +1 -1
- package/dist/client/Mppx.d.ts +12 -7
- package/dist/client/Mppx.d.ts.map +1 -1
- package/dist/client/Mppx.js +10 -5
- package/dist/client/Mppx.js.map +1 -1
- package/dist/client/internal/Fetch.d.ts +13 -11
- package/dist/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +8 -4
- package/dist/client/internal/Fetch.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp-sdk/client/McpClient.d.ts +6 -6
- package/dist/mcp-sdk/client/McpClient.d.ts.map +1 -1
- package/dist/mcp-sdk/client/McpClient.js +4 -4
- package/dist/mcp-sdk/client/McpClient.js.map +1 -1
- package/dist/middlewares/elysia.d.ts +1 -1
- package/dist/middlewares/express.d.ts +1 -1
- package/dist/middlewares/hono.d.ts +1 -1
- package/dist/middlewares/internal/mppx.d.ts +7 -7
- package/dist/middlewares/internal/mppx.d.ts.map +1 -1
- package/dist/middlewares/internal/mppx.js +5 -5
- package/dist/middlewares/internal/mppx.js.map +1 -1
- package/dist/middlewares/nextjs.d.ts +1 -1
- package/dist/proxy/Service.js +2 -2
- package/dist/proxy/Service.js.map +1 -1
- package/dist/server/Methods.d.ts +2 -2
- package/dist/server/Methods.d.ts.map +1 -1
- package/dist/server/Methods.js +2 -2
- package/dist/server/Methods.js.map +1 -1
- package/dist/server/Mppx.d.ts +17 -17
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +9 -9
- package/dist/server/Mppx.js.map +1 -1
- package/dist/stripe/{Intents.d.ts → Methods.d.ts} +22 -22
- package/dist/stripe/Methods.d.ts.map +1 -0
- package/dist/stripe/Methods.js +42 -0
- package/dist/stripe/Methods.js.map +1 -0
- package/dist/stripe/client/Charge.d.ts +48 -44
- package/dist/stripe/client/Charge.d.ts.map +1 -1
- package/dist/stripe/client/Charge.js +22 -17
- package/dist/stripe/client/Charge.js.map +1 -1
- package/dist/stripe/client/{MethodIntents.d.ts → Methods.d.ts} +25 -24
- package/dist/stripe/client/Methods.d.ts.map +1 -0
- package/dist/stripe/client/{MethodIntents.js → Methods.js} +4 -4
- package/dist/stripe/client/Methods.js.map +1 -0
- package/dist/stripe/client/index.d.ts +1 -1
- package/dist/stripe/client/index.d.ts.map +1 -1
- package/dist/stripe/client/index.js +1 -1
- package/dist/stripe/client/index.js.map +1 -1
- package/dist/stripe/index.d.ts +1 -1
- package/dist/stripe/index.d.ts.map +1 -1
- package/dist/stripe/index.js +1 -1
- package/dist/stripe/index.js.map +1 -1
- package/dist/stripe/internal/types.d.ts +25 -0
- package/dist/stripe/internal/types.d.ts.map +1 -0
- package/dist/stripe/internal/types.js +2 -0
- package/dist/stripe/internal/types.js.map +1 -0
- package/dist/stripe/server/Charge.d.ts +47 -28
- package/dist/stripe/server/Charge.d.ts.map +1 -1
- package/dist/stripe/server/Charge.js +90 -32
- package/dist/stripe/server/Charge.js.map +1 -1
- package/dist/stripe/server/{MethodIntents.d.ts → Methods.d.ts} +24 -23
- package/dist/stripe/server/Methods.d.ts.map +1 -0
- package/dist/stripe/server/{MethodIntents.js → Methods.js} +3 -3
- package/dist/stripe/server/Methods.js.map +1 -0
- package/dist/stripe/server/index.d.ts +1 -1
- package/dist/stripe/server/index.d.ts.map +1 -1
- package/dist/stripe/server/index.js +1 -1
- package/dist/stripe/server/index.js.map +1 -1
- package/dist/tempo/{Intents.d.ts → Methods.d.ts} +72 -69
- package/dist/tempo/Methods.d.ts.map +1 -0
- package/dist/tempo/Methods.js +118 -0
- package/dist/tempo/Methods.js.map +1 -0
- package/dist/tempo/client/ChannelOps.d.ts +1 -1
- package/dist/tempo/client/ChannelOps.js +1 -1
- package/dist/tempo/client/Charge.d.ts +25 -25
- package/dist/tempo/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +3 -3
- package/dist/tempo/client/Charge.js.map +1 -1
- package/dist/tempo/client/{MethodIntents.d.ts → Methods.d.ts} +74 -70
- package/dist/tempo/client/Methods.d.ts.map +1 -0
- package/dist/tempo/client/{MethodIntents.js → Methods.js} +3 -3
- package/dist/tempo/client/Methods.js.map +1 -0
- package/dist/tempo/client/Session.d.ts +49 -45
- package/dist/tempo/client/Session.d.ts.map +1 -1
- package/dist/tempo/client/Session.js +4 -4
- package/dist/tempo/client/Session.js.map +1 -1
- package/dist/tempo/client/SessionManager.d.ts +1 -1
- package/dist/tempo/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/client/SessionManager.js +10 -5
- package/dist/tempo/client/SessionManager.js.map +1 -1
- package/dist/tempo/client/index.d.ts +1 -1
- package/dist/tempo/client/index.d.ts.map +1 -1
- package/dist/tempo/client/index.js +1 -1
- package/dist/tempo/client/index.js.map +1 -1
- package/dist/tempo/index.d.ts +1 -1
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +1 -1
- package/dist/tempo/index.js.map +1 -1
- package/dist/tempo/internal/defaults.d.ts +1 -1
- package/dist/tempo/internal/defaults.js +1 -1
- package/dist/tempo/server/Charge.d.ts +27 -27
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +3 -3
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/{MethodIntents.d.ts → Methods.d.ts} +73 -69
- package/dist/tempo/server/Methods.d.ts.map +1 -0
- package/dist/tempo/server/{MethodIntents.js → Methods.js} +4 -4
- package/dist/tempo/server/Methods.js.map +1 -0
- package/dist/tempo/server/Session.d.ts +51 -47
- package/dist/tempo/server/Session.d.ts.map +1 -1
- package/dist/tempo/server/Session.js +4 -4
- package/dist/tempo/server/Session.js.map +1 -1
- package/dist/tempo/server/index.d.ts +6 -0
- package/dist/tempo/server/index.d.ts.map +1 -0
- package/dist/tempo/server/index.js +6 -0
- package/dist/tempo/server/index.js.map +1 -0
- package/dist/tempo/server/internal/transport.d.ts.map +1 -1
- package/dist/tempo/server/internal/transport.js +2 -1
- package/dist/tempo/server/internal/transport.js.map +1 -1
- package/package.json +1 -1
- package/src/Challenge.test-d.ts +3 -3
- package/src/Challenge.test.ts +6 -6
- package/src/Challenge.ts +34 -34
- package/src/Errors.test.ts +75 -21
- package/src/Errors.ts +74 -9
- package/src/Method.test.ts +76 -0
- package/src/Method.ts +228 -0
- package/src/PaymentRequest.test.ts +4 -4
- package/src/PaymentRequest.ts +9 -9
- package/src/cli.test.ts +12 -22
- package/src/cli.ts +74 -21
- package/src/client/Methods.ts +2 -2
- package/src/client/Mppx.test-d.ts +6 -6
- package/src/client/Mppx.test.ts +26 -22
- package/src/client/Mppx.ts +29 -13
- package/src/client/Transport.test.ts +2 -2
- package/src/client/internal/Fetch.test.ts +35 -1
- package/src/client/internal/Fetch.ts +36 -27
- package/src/index.ts +1 -2
- package/src/mcp-sdk/client/McpClient.ts +11 -13
- package/src/middlewares/elysia.ts +1 -1
- package/src/middlewares/express.ts +1 -1
- package/src/middlewares/hono.ts +1 -1
- package/src/middlewares/internal/mppx.ts +10 -10
- package/src/middlewares/nextjs.ts +1 -1
- package/src/proxy/Service.ts +2 -2
- package/src/server/Methods.ts +2 -2
- package/src/server/Mppx.test-d.ts +27 -29
- package/src/server/Mppx.test.ts +23 -19
- package/src/server/Mppx.ts +43 -43
- package/src/server/Transport.test.ts +3 -3
- package/src/stripe/Charge.integration.test.ts +4 -1
- package/src/stripe/{Intents.test.ts → Methods.test.ts} +12 -12
- package/src/stripe/Methods.ts +45 -0
- package/src/stripe/client/Charge.test.ts +189 -0
- package/src/stripe/client/Charge.ts +40 -31
- package/src/stripe/client/{MethodIntents.ts → Methods.ts} +3 -3
- package/src/stripe/client/index.ts +1 -1
- package/src/stripe/index.ts +1 -1
- package/src/stripe/internal/types.ts +22 -0
- package/src/stripe/server/Charge.test.ts +241 -0
- package/src/stripe/server/Charge.ts +124 -38
- package/src/stripe/server/{MethodIntents.ts → Methods.ts} +2 -2
- package/src/stripe/server/index.ts +1 -1
- package/src/tempo/{Intents.test.ts → Methods.test.ts} +15 -15
- package/src/tempo/{Intents.ts → Methods.ts} +77 -22
- package/src/tempo/client/ChannelOps.ts +1 -1
- package/src/tempo/client/Charge.ts +3 -3
- package/src/tempo/client/{MethodIntents.ts → Methods.ts} +2 -2
- package/src/tempo/client/Session.ts +4 -4
- package/src/tempo/client/SessionManager.ts +11 -5
- package/src/tempo/client/index.ts +1 -1
- package/src/tempo/index.ts +1 -1
- package/src/tempo/internal/defaults.ts +1 -1
- package/src/tempo/server/Charge.ts +4 -7
- package/src/tempo/server/{MethodIntents.ts → Methods.ts} +3 -3
- package/src/tempo/server/Session.test.ts +4 -7
- package/src/tempo/server/Session.ts +6 -6
- package/src/tempo/server/index.ts +1 -1
- package/src/tempo/server/internal/transport.ts +3 -2
- package/dist/Intent.d.ts +0 -101
- package/dist/Intent.d.ts.map +0 -1
- package/dist/Intent.js +0 -83
- package/dist/Intent.js.map +0 -1
- package/dist/MethodIntent.d.ts +0 -225
- package/dist/MethodIntent.d.ts.map +0 -1
- package/dist/MethodIntent.js +0 -156
- package/dist/MethodIntent.js.map +0 -1
- package/dist/stripe/Intents.d.ts.map +0 -1
- package/dist/stripe/Intents.js +0 -27
- package/dist/stripe/Intents.js.map +0 -1
- package/dist/stripe/client/MethodIntents.d.ts.map +0 -1
- package/dist/stripe/client/MethodIntents.js.map +0 -1
- package/dist/stripe/server/MethodIntents.d.ts.map +0 -1
- package/dist/stripe/server/MethodIntents.js.map +0 -1
- package/dist/tempo/Intents.d.ts.map +0 -1
- package/dist/tempo/Intents.js +0 -81
- package/dist/tempo/Intents.js.map +0 -1
- package/dist/tempo/client/MethodIntents.d.ts.map +0 -1
- package/dist/tempo/client/MethodIntents.js.map +0 -1
- package/dist/tempo/server/MethodIntents.d.ts.map +0 -1
- package/dist/tempo/server/MethodIntents.js.map +0 -1
- package/src/Intent.test.ts +0 -180
- package/src/Intent.ts +0 -109
- package/src/MethodIntent.test.ts +0 -303
- package/src/MethodIntent.ts +0 -388
- 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 {
|
|
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(
|
|
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(
|
|
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(
|
|
65
|
+
PaymentRequest.fromIntent(Methods.charge, {
|
|
66
66
|
amount: 123,
|
|
67
67
|
currency: '0x20c0000000000000000000000000000000000001',
|
|
68
68
|
recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00',
|
package/src/PaymentRequest.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Base64 } from 'ox'
|
|
2
2
|
import type { Compute } from './internal/types.js'
|
|
3
|
-
import type * as
|
|
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
|
|
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 {
|
|
72
|
+
* import { Methods } from 'mppx/tempo'
|
|
73
73
|
*
|
|
74
|
-
* const request = Request.fromIntent(
|
|
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
|
|
84
|
-
|
|
85
|
-
request: z.input<
|
|
86
|
-
): Request<z.output<
|
|
87
|
-
return
|
|
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/
|
|
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, '
|
|
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, '
|
|
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(
|
|
231
|
-
|
|
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
|
-
|
|
327
|
-
|
|
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.
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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(
|
|
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' &&
|
|
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 === '
|
|
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 (
|
|
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 &&
|
|
644
|
+
if (shouldClose && options.confirm) {
|
|
609
645
|
info('\n')
|
|
610
646
|
}
|
|
611
|
-
if (shouldClose &&
|
|
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
|
-
|
|
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
|
-
|
|
1154
|
+
const hint = defaultYes ? '(Y/n)' : '(y/N)'
|
|
1155
|
+
reader.question(`${pc.bold(`▸ ${prompt}`)} ${pc.dim(hint)} `, (answer) => {
|
|
1104
1156
|
reader.close()
|
|
1105
|
-
|
|
1157
|
+
const trimmed = answer.trim().toLowerCase()
|
|
1158
|
+
resolve(trimmed === '' ? defaultYes : trimmed === 'y')
|
|
1106
1159
|
})
|
|
1107
1160
|
})
|
|
1108
1161
|
}
|
package/src/client/Methods.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { stripe } from '../stripe/client/
|
|
2
|
-
export { tempo } from '../tempo/client/
|
|
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
|
|
3
|
+
import * as Method from '../Method.js'
|
|
4
4
|
import { charge } from '../tempo/client/Charge.js'
|
|
5
|
-
import * as
|
|
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
|
|
17
|
-
expectTypeOf(mppx.methods[0]?.
|
|
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
|
-
|
|
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
|
-
|
|
55
|
+
Method.toClient(Methods.charge, {
|
|
56
56
|
context: z.object({
|
|
57
57
|
account: z.custom<Account>(),
|
|
58
58
|
extra: z.optional(z.string()),
|