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/client/Mppx.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Challenge, Credential,
|
|
1
|
+
import { Challenge, Credential, Mcp, Method, Receipt } from 'mppx'
|
|
2
2
|
import { Mppx, Transport, tempo } from 'mppx/client'
|
|
3
3
|
import { Mppx as Mppx_server, tempo as tempo_server } from 'mppx/server'
|
|
4
|
-
import {
|
|
4
|
+
import { Methods } from 'mppx/tempo'
|
|
5
5
|
import { afterEach, describe, expect, test } from 'vitest'
|
|
6
6
|
import * as Http from '~test/Http.js'
|
|
7
7
|
import { accounts, asset, client } from '~test/tempo/viem.js'
|
|
@@ -21,10 +21,10 @@ describe('Mppx.create', () => {
|
|
|
21
21
|
})
|
|
22
22
|
|
|
23
23
|
expect(mppx.methods).toHaveLength(2)
|
|
24
|
-
expect(mppx.methods[0]?.
|
|
25
|
-
expect(mppx.methods[0]?.
|
|
26
|
-
expect(mppx.methods[1]?.
|
|
27
|
-
expect(mppx.methods[1]?.
|
|
24
|
+
expect(mppx.methods[0]?.name).toBe('tempo')
|
|
25
|
+
expect(mppx.methods[0]?.intent).toBe('charge')
|
|
26
|
+
expect(mppx.methods[1]?.name).toBe('tempo')
|
|
27
|
+
expect(mppx.methods[1]?.intent).toBe('session')
|
|
28
28
|
expect(mppx.transport.name).toBe('http')
|
|
29
29
|
expect(typeof mppx.createCredential).toBe('function')
|
|
30
30
|
expect(typeof mppx.fetch).toBe('function')
|
|
@@ -41,15 +41,17 @@ describe('Mppx.create', () => {
|
|
|
41
41
|
})
|
|
42
42
|
|
|
43
43
|
test('behavior: with multiple methods', () => {
|
|
44
|
-
const stripeCharge =
|
|
45
|
-
|
|
44
|
+
const stripeCharge = Method.from({
|
|
45
|
+
name: 'stripe',
|
|
46
|
+
intent: 'charge',
|
|
46
47
|
schema: {
|
|
47
48
|
credential: {
|
|
48
|
-
payload:
|
|
49
|
+
payload: Methods.charge.schema.credential.payload,
|
|
49
50
|
},
|
|
51
|
+
request: Methods.charge.schema.request,
|
|
50
52
|
},
|
|
51
53
|
})
|
|
52
|
-
const stripeMethod =
|
|
54
|
+
const stripeMethod = Method.toClient(stripeCharge, {
|
|
53
55
|
async createCredential({ challenge }) {
|
|
54
56
|
return Credential.serialize({
|
|
55
57
|
challenge,
|
|
@@ -64,9 +66,9 @@ describe('Mppx.create', () => {
|
|
|
64
66
|
})
|
|
65
67
|
|
|
66
68
|
expect(mppx.methods).toHaveLength(3)
|
|
67
|
-
expect(mppx.methods[0]?.
|
|
68
|
-
expect(mppx.methods[1]?.
|
|
69
|
-
expect(mppx.methods[2]?.
|
|
69
|
+
expect(mppx.methods[0]?.name).toBe('tempo')
|
|
70
|
+
expect(mppx.methods[1]?.name).toBe('tempo')
|
|
71
|
+
expect(mppx.methods[2]?.name).toBe('stripe')
|
|
70
72
|
})
|
|
71
73
|
})
|
|
72
74
|
|
|
@@ -77,7 +79,7 @@ describe('createCredential', () => {
|
|
|
77
79
|
methods: [tempo({ account: accounts[1], getClient: () => client })],
|
|
78
80
|
})
|
|
79
81
|
|
|
80
|
-
const challenge = Challenge.fromIntent(
|
|
82
|
+
const challenge = Challenge.fromIntent(Methods.charge, {
|
|
81
83
|
realm,
|
|
82
84
|
secretKey,
|
|
83
85
|
request: {
|
|
@@ -127,21 +129,23 @@ describe('createCredential', () => {
|
|
|
127
129
|
})
|
|
128
130
|
|
|
129
131
|
await expect(mppx.createCredential(response)).rejects.toThrow(
|
|
130
|
-
'No method
|
|
132
|
+
'No method found for "unknown.charge". Available: tempo.charge, tempo.session',
|
|
131
133
|
)
|
|
132
134
|
})
|
|
133
135
|
|
|
134
136
|
test('behavior: routes to correct method with multiple methods', async () => {
|
|
135
|
-
const stripeCharge =
|
|
136
|
-
|
|
137
|
+
const stripeCharge = Method.from({
|
|
138
|
+
name: 'stripe',
|
|
139
|
+
intent: 'charge',
|
|
137
140
|
schema: {
|
|
138
141
|
credential: {
|
|
139
|
-
payload:
|
|
142
|
+
payload: Methods.charge.schema.credential.payload,
|
|
140
143
|
},
|
|
144
|
+
request: Methods.charge.schema.request,
|
|
141
145
|
},
|
|
142
146
|
})
|
|
143
147
|
|
|
144
|
-
const stripe =
|
|
148
|
+
const stripe = Method.toClient(stripeCharge, {
|
|
145
149
|
async createCredential({ challenge }) {
|
|
146
150
|
return Credential.serialize({
|
|
147
151
|
challenge,
|
|
@@ -188,7 +192,7 @@ describe('createCredential', () => {
|
|
|
188
192
|
methods: [tempo({ getClient: () => client })],
|
|
189
193
|
})
|
|
190
194
|
|
|
191
|
-
const challenge = Challenge.fromIntent(
|
|
195
|
+
const challenge = Challenge.fromIntent(Methods.charge, {
|
|
192
196
|
realm,
|
|
193
197
|
secretKey,
|
|
194
198
|
request: {
|
|
@@ -220,7 +224,7 @@ describe('createCredential', () => {
|
|
|
220
224
|
methods: [tempo({ account: accounts[1], getClient: () => client })],
|
|
221
225
|
})
|
|
222
226
|
|
|
223
|
-
const challenge = Challenge.fromIntent(
|
|
227
|
+
const challenge = Challenge.fromIntent(Methods.charge, {
|
|
224
228
|
realm,
|
|
225
229
|
secretKey,
|
|
226
230
|
request: {
|
|
@@ -251,7 +255,7 @@ describe('createCredential', () => {
|
|
|
251
255
|
transport: Transport.mcp(),
|
|
252
256
|
})
|
|
253
257
|
|
|
254
|
-
const challenge = Challenge.fromIntent(
|
|
258
|
+
const challenge = Challenge.fromIntent(Methods.charge, {
|
|
255
259
|
realm,
|
|
256
260
|
secretKey,
|
|
257
261
|
request: {
|
package/src/client/Mppx.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type * as Challenge from '../Challenge.js'
|
|
2
|
-
import type * as
|
|
2
|
+
import type * as Method from '../Method.js'
|
|
3
3
|
import type * as z from '../zod.js'
|
|
4
4
|
import * as Fetch from './internal/Fetch.js'
|
|
5
5
|
import * as Transport from './Transport.js'
|
|
6
6
|
|
|
7
|
-
export type Methods = readonly (
|
|
7
|
+
export type Methods = readonly (Method.AnyClient | readonly Method.AnyClient[])[]
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Client-side payment handler.
|
|
@@ -27,7 +27,7 @@ export type Mppx<
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* Creates a client-side payment handler from an array of
|
|
30
|
+
* Creates a client-side payment handler from an array of methods.
|
|
31
31
|
*
|
|
32
32
|
* Returns a payment handler with a `fetch` function that automatically handles
|
|
33
33
|
* 402 Payment Required responses. By default, also polyfills `globalThis.fetch`.
|
|
@@ -54,12 +54,19 @@ export function create<
|
|
|
54
54
|
Response
|
|
55
55
|
>,
|
|
56
56
|
>(config: create.Config<methods, transport>): Mppx<methods, transport> {
|
|
57
|
-
const { polyfill = true, transport = Transport.http() as transport } = config
|
|
57
|
+
const { onChallenge, polyfill = true, transport = Transport.http() as transport } = config
|
|
58
58
|
|
|
59
59
|
const methods = config.methods.flat() as unknown as FlattenMethods<methods>
|
|
60
60
|
|
|
61
|
-
const
|
|
62
|
-
|
|
61
|
+
const resolvedOnChallenge = onChallenge as Fetch.from.Config<
|
|
62
|
+
FlattenMethods<methods>
|
|
63
|
+
>['onChallenge']
|
|
64
|
+
const config_fetch = {
|
|
65
|
+
...(config.fetch && { fetch: config.fetch }),
|
|
66
|
+
...(resolvedOnChallenge && { onChallenge: resolvedOnChallenge }),
|
|
67
|
+
methods,
|
|
68
|
+
} satisfies Fetch.from.Config<FlattenMethods<methods>>
|
|
69
|
+
const fetch = Fetch.from<FlattenMethods<methods>>(config_fetch)
|
|
63
70
|
|
|
64
71
|
if (polyfill) Fetch.polyfill(config_fetch)
|
|
65
72
|
return {
|
|
@@ -69,10 +76,10 @@ export function create<
|
|
|
69
76
|
async createCredential(response: Transport.ResponseOf<transport>, context?: unknown) {
|
|
70
77
|
const challenge = transport.getChallenge(response as never) as Challenge.Challenge
|
|
71
78
|
|
|
72
|
-
const mi = methods.find((m) => m.
|
|
79
|
+
const mi = methods.find((m) => m.name === challenge.method && m.intent === challenge.intent)
|
|
73
80
|
if (!mi)
|
|
74
81
|
throw new Error(
|
|
75
|
-
`No method
|
|
82
|
+
`No method found for "${challenge.method}.${challenge.intent}". Available: ${methods.map((m) => `${m.name}.${m.intent}`).join(', ')}`,
|
|
76
83
|
)
|
|
77
84
|
|
|
78
85
|
const parsedContext =
|
|
@@ -112,7 +119,16 @@ export declare namespace create {
|
|
|
112
119
|
> = {
|
|
113
120
|
/** Custom fetch function to wrap. Defaults to `globalThis.fetch`. */
|
|
114
121
|
fetch?: typeof globalThis.fetch
|
|
115
|
-
/**
|
|
122
|
+
/** Called when a 402 challenge is received, before credential creation. */
|
|
123
|
+
onChallenge?:
|
|
124
|
+
| ((
|
|
125
|
+
challenge: Challenge.Challenge,
|
|
126
|
+
helpers: {
|
|
127
|
+
createCredential: (context?: AnyContextFor<FlattenMethods<methods>>) => Promise<string>
|
|
128
|
+
},
|
|
129
|
+
) => Promise<string | undefined>)
|
|
130
|
+
| undefined
|
|
131
|
+
/** Array of methods to use. Accepts individual clients or tuples (e.g. from `tempo()`). */
|
|
116
132
|
methods: methods
|
|
117
133
|
/** Whether to polyfill `globalThis.fetch` with the payment-aware wrapper. @default true */
|
|
118
134
|
polyfill?: boolean | undefined
|
|
@@ -125,8 +141,8 @@ export declare namespace create {
|
|
|
125
141
|
* Union of all context types from all methods that have context schemas.
|
|
126
142
|
* @internal
|
|
127
143
|
*/
|
|
128
|
-
type AnyContextFor<methods extends readonly
|
|
129
|
-
[method in keyof methods]: methods[method] extends
|
|
144
|
+
type AnyContextFor<methods extends readonly Method.AnyClient[]> = {
|
|
145
|
+
[method in keyof methods]: methods[method] extends Method.Client<any, infer context>
|
|
130
146
|
? context extends z.ZodMiniType
|
|
131
147
|
? z.input<context>
|
|
132
148
|
: undefined
|
|
@@ -141,9 +157,9 @@ type FlattenMethods<methods extends Methods> = methods extends readonly [
|
|
|
141
157
|
infer head,
|
|
142
158
|
...infer tail extends Methods,
|
|
143
159
|
]
|
|
144
|
-
? head extends readonly
|
|
160
|
+
? head extends readonly Method.AnyClient[]
|
|
145
161
|
? readonly [...head, ...FlattenMethods<tail>]
|
|
146
|
-
: head extends
|
|
162
|
+
: head extends Method.AnyClient
|
|
147
163
|
? readonly [head, ...FlattenMethods<tail>]
|
|
148
164
|
: never
|
|
149
165
|
: readonly []
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Challenge, Credential, Mcp } from 'mppx'
|
|
2
2
|
import { Transport } from 'mppx/client'
|
|
3
|
-
import {
|
|
3
|
+
import { Methods } from 'mppx/tempo'
|
|
4
4
|
import { describe, expect, test } from 'vitest'
|
|
5
5
|
|
|
6
6
|
const realm = 'api.example.com'
|
|
7
7
|
const secretKey = 'test-secret-key'
|
|
8
8
|
|
|
9
|
-
const challenge = Challenge.fromIntent(
|
|
9
|
+
const challenge = Challenge.fromIntent(Methods.charge, {
|
|
10
10
|
realm,
|
|
11
11
|
secretKey,
|
|
12
12
|
request: {
|
|
@@ -2,7 +2,7 @@ import { Receipt } from 'mppx'
|
|
|
2
2
|
import { tempo } from 'mppx/client'
|
|
3
3
|
import { Mppx as Mppx_server, tempo as tempo_server } from 'mppx/server'
|
|
4
4
|
import { createClient } from 'viem'
|
|
5
|
-
import { describe, expect, test } from 'vitest'
|
|
5
|
+
import { describe, expect, test, vi } from 'vitest'
|
|
6
6
|
import * as Http from '~test/Http.js'
|
|
7
7
|
import { accounts, asset, chain, client, http } from '~test/tempo/viem.js'
|
|
8
8
|
import * as Fetch from './Fetch.js'
|
|
@@ -232,6 +232,40 @@ describe('Fetch.from', () => {
|
|
|
232
232
|
|
|
233
233
|
httpServer.close()
|
|
234
234
|
})
|
|
235
|
+
|
|
236
|
+
test('behavior: onChallenge can create credential', async () => {
|
|
237
|
+
const onChallenge = vi.fn(async (_challenge, { createCredential }) =>
|
|
238
|
+
createCredential({ account: accounts[1] }),
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
const fetch = Fetch.from({
|
|
242
|
+
methods: [
|
|
243
|
+
tempo.charge({
|
|
244
|
+
getClient: () => client,
|
|
245
|
+
}),
|
|
246
|
+
],
|
|
247
|
+
onChallenge,
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
const httpServer = await Http.createServer(async (req, res) => {
|
|
251
|
+
const result = await Mppx_server.toNodeListener(
|
|
252
|
+
server.charge({
|
|
253
|
+
amount: '1',
|
|
254
|
+
currency: asset,
|
|
255
|
+
expires: new Date(Date.now() + 60_000).toISOString(),
|
|
256
|
+
recipient: accounts[0].address,
|
|
257
|
+
}),
|
|
258
|
+
)(req, res)
|
|
259
|
+
if (result.status === 402) return
|
|
260
|
+
res.end('OK')
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
const response = await fetch(httpServer.url)
|
|
264
|
+
expect(response.status).toBe(200)
|
|
265
|
+
expect(onChallenge).toHaveBeenCalledTimes(1)
|
|
266
|
+
|
|
267
|
+
httpServer.close()
|
|
268
|
+
})
|
|
235
269
|
})
|
|
236
270
|
|
|
237
271
|
describe('Fetch.polyfill', () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Challenge from '../../Challenge.js'
|
|
2
|
-
import type * as
|
|
2
|
+
import type * as Method from '../../Method.js'
|
|
3
3
|
import type * as z from '../../zod.js'
|
|
4
4
|
|
|
5
5
|
let originalFetch: typeof globalThis.fetch | undefined
|
|
@@ -25,7 +25,7 @@ let originalFetch: typeof globalThis.fetch | undefined
|
|
|
25
25
|
* ```
|
|
26
26
|
*
|
|
27
27
|
*/
|
|
28
|
-
export function from<const methods extends readonly
|
|
28
|
+
export function from<const methods extends readonly Method.AnyClient[]>(
|
|
29
29
|
config: from.Config<methods>,
|
|
30
30
|
): from.Fetch<methods> {
|
|
31
31
|
const { fetch = globalThis.fetch, methods, onChallenge } = config
|
|
@@ -37,15 +37,20 @@ export function from<const methods extends readonly MethodIntent.AnyClient[]>(
|
|
|
37
37
|
if (response.status !== 402) return response
|
|
38
38
|
|
|
39
39
|
const challenge = Challenge.fromResponse(response)
|
|
40
|
-
onChallenge?.(challenge)
|
|
41
40
|
|
|
42
|
-
const mi = methods.find((m) => m.
|
|
41
|
+
const mi = methods.find((m) => m.name === challenge.method && m.intent === challenge.intent)
|
|
43
42
|
if (!mi)
|
|
44
43
|
throw new Error(
|
|
45
|
-
`No method
|
|
44
|
+
`No method found for "${challenge.method}.${challenge.intent}". Available: ${methods.map((m) => `${m.name}.${m.intent}`).join(', ')}`,
|
|
46
45
|
)
|
|
47
46
|
|
|
48
|
-
const
|
|
47
|
+
const onChallengeCredential = onChallenge
|
|
48
|
+
? await onChallenge(challenge, {
|
|
49
|
+
createCredential: async (overrideContext?: AnyContextFor<methods>) =>
|
|
50
|
+
resolveCredential(challenge, mi, overrideContext ?? context),
|
|
51
|
+
})
|
|
52
|
+
: undefined
|
|
53
|
+
const credential = onChallengeCredential ?? (await resolveCredential(challenge, mi, context))
|
|
49
54
|
|
|
50
55
|
return fetch(input, {
|
|
51
56
|
...fetchInit,
|
|
@@ -58,8 +63,8 @@ export function from<const methods extends readonly MethodIntent.AnyClient[]>(
|
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
/** Union of all context types from all methods that have context schemas. */
|
|
61
|
-
type AnyContextFor<methods extends readonly
|
|
62
|
-
[K in keyof methods]: methods[K] extends
|
|
66
|
+
type AnyContextFor<methods extends readonly Method.AnyClient[]> = {
|
|
67
|
+
[K in keyof methods]: methods[K] extends Method.Client<any, infer contextSchema>
|
|
63
68
|
? contextSchema extends z.ZodMiniType
|
|
64
69
|
? z.input<contextSchema>
|
|
65
70
|
: undefined
|
|
@@ -67,27 +72,32 @@ type AnyContextFor<methods extends readonly MethodIntent.AnyClient[]> = {
|
|
|
67
72
|
}[number]
|
|
68
73
|
|
|
69
74
|
export declare namespace from {
|
|
70
|
-
type Config<
|
|
71
|
-
methods extends readonly MethodIntent.AnyClient[] = readonly MethodIntent.AnyClient[],
|
|
72
|
-
> = {
|
|
75
|
+
type Config<methods extends readonly Method.AnyClient[] = readonly Method.AnyClient[]> = {
|
|
73
76
|
/** Custom fetch function to wrap. Defaults to `globalThis.fetch`. */
|
|
74
77
|
fetch?: typeof globalThis.fetch
|
|
75
|
-
/** Array of
|
|
78
|
+
/** Array of methods to use. */
|
|
76
79
|
methods: methods
|
|
77
80
|
/** Called when a 402 challenge is received, before credential creation. */
|
|
78
|
-
onChallenge?:
|
|
81
|
+
onChallenge?:
|
|
82
|
+
| ((
|
|
83
|
+
challenge: Challenge.Challenge,
|
|
84
|
+
helpers: {
|
|
85
|
+
createCredential: (context?: AnyContextFor<methods>) => Promise<string>
|
|
86
|
+
},
|
|
87
|
+
) => Promise<string | undefined>)
|
|
88
|
+
| undefined
|
|
79
89
|
}
|
|
80
90
|
|
|
81
|
-
type Fetch<
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
type Fetch<methods extends readonly Method.AnyClient[] = readonly Method.AnyClient[]> = (
|
|
92
|
+
input: RequestInfo | URL,
|
|
93
|
+
init?: RequestInit<methods>,
|
|
94
|
+
) => Promise<Response>
|
|
84
95
|
|
|
85
|
-
type RequestInit<
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
96
|
+
type RequestInit<methods extends readonly Method.AnyClient[] = readonly Method.AnyClient[]> =
|
|
97
|
+
globalThis.RequestInit & {
|
|
98
|
+
/** Context to pass to the method intent's createCredential. */
|
|
99
|
+
context?: AnyContextFor<methods>
|
|
100
|
+
}
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
/**
|
|
@@ -110,7 +120,7 @@ export declare namespace from {
|
|
|
110
120
|
* const res = await fetch('https://api.example.com/resource')
|
|
111
121
|
* ```
|
|
112
122
|
*/
|
|
113
|
-
export function polyfill<const methods extends readonly
|
|
123
|
+
export function polyfill<const methods extends readonly Method.AnyClient[]>(
|
|
114
124
|
config: polyfill.Config<methods>,
|
|
115
125
|
): void {
|
|
116
126
|
originalFetch = globalThis.fetch
|
|
@@ -118,9 +128,8 @@ export function polyfill<const methods extends readonly MethodIntent.AnyClient[]
|
|
|
118
128
|
}
|
|
119
129
|
|
|
120
130
|
export declare namespace polyfill {
|
|
121
|
-
type Config<
|
|
122
|
-
methods
|
|
123
|
-
> = from.Config<methods>
|
|
131
|
+
type Config<methods extends readonly Method.AnyClient[] = readonly Method.AnyClient[]> =
|
|
132
|
+
from.Config<methods>
|
|
124
133
|
}
|
|
125
134
|
|
|
126
135
|
/**
|
|
@@ -147,7 +156,7 @@ export function restore(): void {
|
|
|
147
156
|
/** @internal */
|
|
148
157
|
async function resolveCredential(
|
|
149
158
|
challenge: Challenge.Challenge,
|
|
150
|
-
mi:
|
|
159
|
+
mi: Method.AnyClient,
|
|
151
160
|
context: unknown,
|
|
152
161
|
): Promise<string> {
|
|
153
162
|
const parsedContext = mi.context && context !== undefined ? mi.context.parse(context) : undefined
|
package/src/index.ts
CHANGED
|
@@ -3,9 +3,8 @@ export * as Challenge from './Challenge.js'
|
|
|
3
3
|
export * as Credential from './Credential.js'
|
|
4
4
|
export * as Errors from './Errors.js'
|
|
5
5
|
export * as Expires from './Expires.js'
|
|
6
|
-
export * as Intent from './Intent.js'
|
|
7
6
|
export * as Mcp from './Mcp.js'
|
|
8
|
-
export * as
|
|
7
|
+
export * as Method from './Method.js'
|
|
9
8
|
export * as PaymentRequest from './PaymentRequest.js'
|
|
10
9
|
export * as Receipt from './Receipt.js'
|
|
11
10
|
export * as Store from './Store.js'
|
|
@@ -3,10 +3,10 @@ import type { McpError } from '@modelcontextprotocol/sdk/types.js'
|
|
|
3
3
|
import type * as Challenge from '../../Challenge.js'
|
|
4
4
|
import * as Credential from '../../Credential.js'
|
|
5
5
|
import * as core_Mcp from '../../Mcp.js'
|
|
6
|
-
import type * as
|
|
6
|
+
import type * as Method from '../../Method.js'
|
|
7
7
|
import type * as z from '../../zod.js'
|
|
8
8
|
|
|
9
|
-
type AnyClient =
|
|
9
|
+
type AnyClient = Method.Client<any, any>
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Result of a tool call with payment handling.
|
|
@@ -47,7 +47,7 @@ export type CallToolResult = Awaited<ReturnType<Client['callTool']>> & {
|
|
|
47
47
|
*/
|
|
48
48
|
export function wrap<
|
|
49
49
|
const client extends Pick<Client, 'callTool'>,
|
|
50
|
-
const methods extends readonly
|
|
50
|
+
const methods extends readonly Method.AnyClient[],
|
|
51
51
|
>(client: client, config: wrap.Config<methods>): wrap.McpClient<client, methods> {
|
|
52
52
|
const { methods } = config
|
|
53
53
|
|
|
@@ -77,11 +77,11 @@ export function wrap<
|
|
|
77
77
|
|
|
78
78
|
// Select first challenge that matches an installed method intent
|
|
79
79
|
const challenge = challenges.find((c) =>
|
|
80
|
-
methods.some((m) => m.
|
|
80
|
+
methods.some((m) => m.name === c.method && m.intent === c.intent),
|
|
81
81
|
)
|
|
82
82
|
if (!challenge) {
|
|
83
83
|
const available = challenges.map((c) => `${c.method}.${c.intent}`).join(', ')
|
|
84
|
-
const installed = methods.map((m) => `${m.
|
|
84
|
+
const installed = methods.map((m) => `${m.name}.${m.intent}`).join(', ')
|
|
85
85
|
throw new Error(
|
|
86
86
|
`No compatible payment method. Server offers: ${available}. Client has: ${installed}`,
|
|
87
87
|
)
|
|
@@ -113,7 +113,7 @@ export function wrap<
|
|
|
113
113
|
|
|
114
114
|
/** Union of all context types from all methods that have context schemas. */
|
|
115
115
|
type AnyContextFor<methods extends readonly AnyClient[]> = {
|
|
116
|
-
[key in keyof methods]: methods[key] extends
|
|
116
|
+
[key in keyof methods]: methods[key] extends Method.Client<any, infer context>
|
|
117
117
|
? context extends z.ZodMiniType
|
|
118
118
|
? z.input<context>
|
|
119
119
|
: undefined
|
|
@@ -121,10 +121,8 @@ type AnyContextFor<methods extends readonly AnyClient[]> = {
|
|
|
121
121
|
}[number]
|
|
122
122
|
|
|
123
123
|
export declare namespace wrap {
|
|
124
|
-
type Config<
|
|
125
|
-
|
|
126
|
-
> = {
|
|
127
|
-
/** Array of method intents to use. */
|
|
124
|
+
type Config<methods extends readonly Method.AnyClient[] = readonly Method.AnyClient[]> = {
|
|
125
|
+
/** Array of methods to use. */
|
|
128
126
|
methods: methods
|
|
129
127
|
}
|
|
130
128
|
|
|
@@ -165,7 +163,7 @@ export function isPaymentRequiredError(
|
|
|
165
163
|
}
|
|
166
164
|
|
|
167
165
|
/** @internal */
|
|
168
|
-
async function createCredential<methods extends readonly
|
|
166
|
+
async function createCredential<methods extends readonly Method.AnyClient[]>(
|
|
169
167
|
challenge: Challenge.Challenge,
|
|
170
168
|
config: {
|
|
171
169
|
context?: unknown
|
|
@@ -174,10 +172,10 @@ async function createCredential<methods extends readonly MethodIntent.AnyClient[
|
|
|
174
172
|
): Promise<string> {
|
|
175
173
|
const { context, methods } = config
|
|
176
174
|
|
|
177
|
-
const mi = methods.find((m) => m.
|
|
175
|
+
const mi = methods.find((m) => m.name === challenge.method && m.intent === challenge.intent)
|
|
178
176
|
if (!mi)
|
|
179
177
|
throw new Error(
|
|
180
|
-
`No method
|
|
178
|
+
`No method found for "${challenge.method}.${challenge.intent}". Available: ${methods.map((m) => `${m.name}.${m.intent}`).join(', ')}`,
|
|
181
179
|
)
|
|
182
180
|
|
|
183
181
|
const parsedContext = mi.context && context !== undefined ? mi.context.parse(context) : undefined
|
|
@@ -55,7 +55,7 @@ export namespace Mppx {
|
|
|
55
55
|
* )
|
|
56
56
|
* ```
|
|
57
57
|
*/
|
|
58
|
-
export function payment<const intent extends Mppx_internal.
|
|
58
|
+
export function payment<const intent extends Mppx_internal.AnyMethodFn>(
|
|
59
59
|
intent: intent,
|
|
60
60
|
options: intent extends (options: infer options) => any ? options : never,
|
|
61
61
|
): ElysiaHook {
|
|
@@ -55,7 +55,7 @@ export namespace Mppx {
|
|
|
55
55
|
* })
|
|
56
56
|
* ```
|
|
57
57
|
*/
|
|
58
|
-
export function payment<const intent extends Mppx_internal.
|
|
58
|
+
export function payment<const intent extends Mppx_internal.AnyMethodFn>(
|
|
59
59
|
intent: intent,
|
|
60
60
|
options: intent extends (options: infer options) => any ? options : never,
|
|
61
61
|
): RequestHandler {
|
package/src/middlewares/hono.ts
CHANGED
|
@@ -49,7 +49,7 @@ export namespace Mppx {
|
|
|
49
49
|
* )
|
|
50
50
|
* ```
|
|
51
51
|
*/
|
|
52
|
-
export function payment<const intent extends Mppx_internal.
|
|
52
|
+
export function payment<const intent extends Mppx_internal.AnyMethodFn>(
|
|
53
53
|
intent: intent,
|
|
54
54
|
options: intent extends (options: infer options) => any ? options : never,
|
|
55
55
|
): MiddlewareHandler {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type * as
|
|
1
|
+
import type * as Method from '../../Method.js'
|
|
2
2
|
import type * as Mppx from '../../server/Mppx.js'
|
|
3
3
|
|
|
4
|
-
export type
|
|
5
|
-
export type AnyServer =
|
|
4
|
+
export type AnyMethodFn = Mppx.AnyMethodFn
|
|
5
|
+
export type AnyServer = Method.AnyServer
|
|
6
6
|
|
|
7
7
|
export type Wrap<mppx, handler> = {
|
|
8
8
|
[key in keyof mppx]: mppx[key] extends (options: infer options) => any
|
|
@@ -11,20 +11,20 @@ export type Wrap<mppx, handler> = {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Wraps a payment handler so each
|
|
15
|
-
* handler instead of the raw
|
|
14
|
+
* Wraps a payment handler so each method returns a framework-specific
|
|
15
|
+
* handler instead of the raw method response.
|
|
16
16
|
*
|
|
17
17
|
* @param mppx - The payment handler created by `Mppx.create`.
|
|
18
|
-
* @param wrapper - A function that adapts
|
|
18
|
+
* @param wrapper - A function that adapts a method function into a framework handler.
|
|
19
19
|
*/
|
|
20
20
|
export function wrap<mppx extends Mppx.Mppx<any, any>, handler>(
|
|
21
21
|
mppx: mppx,
|
|
22
|
-
wrapper: (
|
|
22
|
+
wrapper: (method: AnyMethodFn, options: any) => handler,
|
|
23
23
|
): Wrap<mppx, handler> {
|
|
24
24
|
const result: Record<string, unknown> = { ...mppx }
|
|
25
|
-
for (const mi of mppx.methods as readonly
|
|
26
|
-
const
|
|
27
|
-
result[mi.
|
|
25
|
+
for (const mi of mppx.methods as readonly Method.AnyServer[]) {
|
|
26
|
+
const methodFn = (mppx as any)[mi.intent]
|
|
27
|
+
result[mi.intent] = (options: any) => wrapper(methodFn, options)
|
|
28
28
|
}
|
|
29
29
|
return result as never
|
|
30
30
|
}
|
|
@@ -52,7 +52,7 @@ export namespace Mppx {
|
|
|
52
52
|
* )
|
|
53
53
|
* ```
|
|
54
54
|
*/
|
|
55
|
-
export function payment<const intent extends Mppx_internal.
|
|
55
|
+
export function payment<const intent extends Mppx_internal.AnyMethodFn>(
|
|
56
56
|
intent: intent,
|
|
57
57
|
options: intent extends (options: infer options) => any ? options : never,
|
|
58
58
|
handler: RouteHandler,
|
package/src/proxy/Service.ts
CHANGED
|
@@ -217,11 +217,11 @@ function resolvePayment(endpoint: Endpoint): Record<string, unknown> | null {
|
|
|
217
217
|
if (endpoint === true) return null
|
|
218
218
|
const handler = typeof endpoint === 'function' ? endpoint : endpoint.pay
|
|
219
219
|
if (!('_internal' in handler)) return {}
|
|
220
|
-
const { name,
|
|
220
|
+
const { name, intent, defaults, schema, ...rest } = handler._internal as Record<string, unknown>
|
|
221
221
|
const amount = (() => {
|
|
222
222
|
if (typeof rest.amount === 'string' && typeof rest.decimals === 'number')
|
|
223
223
|
return String(Value.from(rest.amount, rest.decimals))
|
|
224
224
|
return rest.amount
|
|
225
225
|
})()
|
|
226
|
-
return { intent: name,
|
|
226
|
+
return { intent, method: name, ...rest, ...(amount !== undefined && { amount }) }
|
|
227
227
|
}
|
package/src/server/Methods.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { stripe } from '../stripe/server/
|
|
2
|
-
export { tempo } from '../tempo/server/
|
|
1
|
+
export { stripe } from '../stripe/server/index.js'
|
|
2
|
+
export { tempo } from '../tempo/server/index.js'
|