mppx 0.10.1 → 0.12.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 (56) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +22 -1
  3. package/dist/cli/agent.d.ts +3 -0
  4. package/dist/cli/agent.js +20 -0
  5. package/dist/cli/cli.d.ts +1 -1
  6. package/dist/cli/cli.js +135 -135
  7. package/dist/cli/plugins/index.js +12 -12
  8. package/dist/cli/validate/payment.js +29 -12
  9. package/dist/client/internal/protocols/X402.js +0 -2
  10. package/dist/evm/server/Methods.d.ts +164 -3
  11. package/dist/evm/server/Methods.js +50 -2
  12. package/dist/internal/version.d.ts +2 -2
  13. package/dist/internal/version.js +1 -1
  14. package/dist/server/Mppx.d.ts +5 -1
  15. package/dist/server/Mppx.js +120 -49
  16. package/dist/server/internal/html/compose.main.gen.d.ts +1 -1
  17. package/dist/server/internal/html/serviceWorker.gen.d.ts +1 -1
  18. package/dist/stripe/server/Methods.js +13 -7
  19. package/dist/stripe/server/internal/analytics.d.ts +1 -0
  20. package/dist/stripe/server/internal/analytics.js +2 -2
  21. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  22. package/dist/stripe/server/internal/record-payment.js +2 -2
  23. package/dist/tempo/Methods.d.ts +5 -3
  24. package/dist/tempo/Methods.js +6 -5
  25. package/dist/tempo/client/Charge.d.ts +6 -4
  26. package/dist/tempo/client/Charge.js +61 -28
  27. package/dist/tempo/client/Methods.d.ts +5 -3
  28. package/dist/tempo/index.d.ts +2 -1
  29. package/dist/tempo/index.js +2 -1
  30. package/dist/tempo/internal/currencies.d.ts +19 -0
  31. package/dist/tempo/internal/currencies.js +41 -0
  32. package/dist/tempo/internal/defaults.d.ts +16 -3
  33. package/dist/tempo/internal/defaults.js +17 -2
  34. package/dist/tempo/internal/fee-payer.d.ts +5 -4
  35. package/dist/tempo/internal/fee-payer.js +7 -5
  36. package/dist/tempo/internal/machine-token-charge.d.ts +56 -0
  37. package/dist/tempo/internal/machine-token-charge.js +108 -0
  38. package/dist/tempo/server/Charge.d.ts +15 -1
  39. package/dist/tempo/server/Charge.js +101 -67
  40. package/dist/tempo/server/Methods.d.ts +5008 -40
  41. package/dist/tempo/server/Methods.js +105 -11
  42. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  43. package/dist/tempo/server/internal/html.gen.js +1 -1
  44. package/dist/tempo/session/server/ChannelStore.d.ts +2 -0
  45. package/dist/tempo/session/server/ChannelStore.js +12 -3
  46. package/dist/tempo/session/server/CredentialVerification.d.ts +2 -0
  47. package/dist/tempo/session/server/CredentialVerification.js +22 -13
  48. package/dist/tempo/session/server/RequestState.d.ts +6 -0
  49. package/dist/tempo/session/server/RequestState.js +11 -4
  50. package/dist/tempo/session/server/Session.js +8 -1
  51. package/dist/tempo/session/server/Settlement.d.ts +2 -0
  52. package/dist/tempo/session/server/Settlement.js +13 -8
  53. package/dist/tempo/subscription/KeyAuthorization.d.ts +70 -8
  54. package/package.json +3 -3
  55. package/dist/tempo/Tokens.d.ts +0 -13
  56. package/dist/tempo/Tokens.js +0 -13
@@ -1,5 +1,5 @@
1
1
  import * as childProcess from 'node:child_process';
2
- import { createClient, erc20Abi, http } from 'viem';
2
+ import { createClient, erc20Abi, http, isAddress, zeroAddress, } from 'viem';
3
3
  import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
4
4
  import { readContract, waitForTransactionReceipt } from 'viem/actions';
5
5
  import * as viemChains from 'viem/chains';
@@ -24,6 +24,16 @@ async function provisionAndPayTestnet(challenge, verbose, silent) {
24
24
  const key = generatePrivateKey();
25
25
  const account = privateKeyToAccount(key);
26
26
  const client = createClient({ chain: tempoModerato, transport: http() });
27
+ if (isTempoSessionChallenge(challenge)) {
28
+ const { chainId, operator } = challenge.request.methodDetails ?? {};
29
+ if (chainId === tempoModerato.id &&
30
+ typeof operator === 'string' &&
31
+ isAddress(operator) &&
32
+ operator !== zeroAddress) {
33
+ const hashes = await Actions.faucet.fund(client, { account: operator });
34
+ await Promise.all(hashes.map((hash) => waitForTransactionReceipt(client, { hash })));
35
+ }
36
+ }
27
37
  const hashes = await Actions.faucet.fund(client, { account });
28
38
  await Promise.all(hashes.map((hash) => waitForTransactionReceipt(client, { hash })));
29
39
  if (!silent)
@@ -166,18 +176,25 @@ export async function validatePaymentFlow(baseUrl, endpoint, verbose, options) {
166
176
  if (tempoTestnetChallenge) {
167
177
  const provisioned = await provisionAndPayTestnet(tempoTestnetChallenge, verbose, options.silent);
168
178
  if (provisioned) {
169
- const fakeResp = new Response(null, {
170
- status: 402,
171
- headers: {
172
- [Constants.Headers.wwwAuthenticate]: Challenge.serialize(tempoTestnetChallenge),
173
- },
174
- });
175
179
  try {
176
- const mppx = Mppx.create({ methods: provisioned.methods, polyfill: false });
177
- const credentialContext = await preparePayment(tempoTestnetChallenge, loaded?.config.extensions);
178
- const cred = await mppx.createCredential(fakeResp, credentialContext);
179
- results.push(check('Payment: submitted', 'ephemeral testnet wallet'));
180
- await sendAndValidateResponse(results, url, endpoint, cred, fetchHeaders, fetchBody, verbose, tempoModerato, Challenge.credentialHeader(tempoTestnetChallenge));
180
+ const mppx = Mppx.create({
181
+ methods: provisioned.methods,
182
+ polyfill: false,
183
+ async onChallenge(challenge, { createCredential }) {
184
+ assertSamePaymentRequest(tempoTestnetChallenge, challenge);
185
+ const context = await preparePayment(challenge, loaded?.config.extensions);
186
+ const credential = await createCredential(context);
187
+ results.push(check('Payment: submitted', 'ephemeral testnet wallet'));
188
+ return credential;
189
+ },
190
+ fetch: (input, init) => fetchWithTimeout(input, init ?? {}, 30_000),
191
+ });
192
+ const response = await mppx.fetch(url, {
193
+ method: endpoint.method,
194
+ headers: fetchHeaders,
195
+ body: fetchBody ?? null,
196
+ });
197
+ await validatePaymentResponse(results, response, verbose, tempoModerato);
181
198
  }
182
199
  catch (error) {
183
200
  results.push(fail('Payment: create credential', error.message));
@@ -17,8 +17,6 @@ export function x402() {
17
17
  if (!header)
18
18
  return [];
19
19
  const paymentRequired = x402_Header.decodePaymentRequiredEnvelope(header);
20
- if (response.url && paymentRequired.resource.url !== response.url)
21
- throw new Error('x402 payment-required resource does not match response URL.');
22
20
  return paymentRequired.accepts.flatMap((rawAccepted, index) => {
23
21
  const parsed = x402_Types.PaymentRequirementsSchema.safeParse(rawAccepted);
24
22
  if (!parsed.success)
@@ -2,8 +2,158 @@ import type { NoExtraKeys } from '../../internal/types.js';
2
2
  import * as Assets from '../Assets.js';
3
3
  import * as Chains from '../Chains.js';
4
4
  import { charge as charge_ } from './Charge.js';
5
- /** Creates EVM server methods from shared charge parameters. */
6
- export declare function evm<const parameters extends evm.Parameters>(parameters: NoExtraKeys<parameters, evm.Parameters>): readonly [import("../../Method.js").Server<{
5
+ /**
6
+ * Creates ordered EVM charge offers from shared parameters.
7
+ *
8
+ * Pass `currencies: [token]` for one asset or an ordered list for multiple assets.
9
+ * Viem token definitions resolve against `chainId`; known assets carry their own
10
+ * network. Unsupported chain entries are skipped and duplicate chain/address
11
+ * pairs are removed. An empty resolved list throws.
12
+ *
13
+ * Token definitions need explicit EIP-3009 domain metadata. Use
14
+ * `evm.assets.fromToken(token, { chainId, transfer })` for per-token domains.
15
+ * A token registry entry alone does not establish EIP-3009 support.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { evm } from 'mppx/server'
20
+ *
21
+ * evm({
22
+ * currencies: [evm.assets.base.USDC],
23
+ * recipient: '0x742d35Cc6634c0532925a3b844bC9e7595F8fE00',
24
+ * settle: async ({ payload }) => settleAuthorization(payload),
25
+ * })
26
+ * ```
27
+ */
28
+ export declare function evm<const parameters extends evm.Parameters>(parameters: NoExtraKeys<parameters, evm.Parameters>): [import("../../Method.js").Server<{
29
+ readonly name: "evm";
30
+ readonly intent: "charge";
31
+ readonly schema: {
32
+ credential: {
33
+ payload: import("zod/mini").ZodMiniObject<{
34
+ from: import("zod/mini").ZodMiniString<string>;
35
+ nonce: import("zod/mini").ZodMiniString<string>;
36
+ signature: import("zod/mini").ZodMiniString<string>;
37
+ to: import("zod/mini").ZodMiniString<string>;
38
+ type: import("zod/mini").ZodMiniLiteral<"authorization">;
39
+ validAfter: import("zod/mini").ZodMiniString<string>;
40
+ validBefore: import("zod/mini").ZodMiniString<string>;
41
+ value: import("zod/mini").ZodMiniString<string>;
42
+ }, import("zod/v4/core").$strip>;
43
+ };
44
+ request: import("zod/mini").ZodMiniPipe<import("zod/mini").ZodMiniObject<{
45
+ amount: import("zod/mini").ZodMiniString<string>;
46
+ chainId: import("zod/mini").ZodMiniNumber<number>;
47
+ currency: import("zod/mini").ZodMiniString<string>;
48
+ credentialTypes: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniEnum<{
49
+ authorization: "authorization";
50
+ }>>>;
51
+ decimals: import("zod/mini").ZodMiniNumber<number>;
52
+ description: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
53
+ externalId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
54
+ permit2Address: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
55
+ recipient: import("zod/mini").ZodMiniString<string>;
56
+ splits: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
57
+ amount: import("zod/mini").ZodMiniString<string>;
58
+ recipient: import("zod/mini").ZodMiniString<string>;
59
+ }, import("zod/v4/core").$strip>>>;
60
+ }, import("zod/v4/core").$strip>, import("zod/mini").ZodMiniTransform<{
61
+ description?: string | undefined;
62
+ externalId?: string | undefined;
63
+ amount: string;
64
+ currency: `0x${string}`;
65
+ methodDetails: {
66
+ chainId: number;
67
+ credentialTypes: string[];
68
+ decimals: number;
69
+ permit2Address?: `0x${string}` | undefined;
70
+ splits?: {
71
+ amount: string;
72
+ recipient: string;
73
+ }[] | undefined;
74
+ };
75
+ recipient: `0x${string}`;
76
+ }, {
77
+ amount: string;
78
+ chainId: number;
79
+ currency: string;
80
+ credentialTypes?: "authorization"[] | undefined;
81
+ decimals: number;
82
+ description?: string | undefined;
83
+ externalId?: string | undefined;
84
+ permit2Address?: string | undefined;
85
+ recipient: string;
86
+ splits?: {
87
+ amount: string;
88
+ recipient: string;
89
+ }[] | undefined;
90
+ }>>;
91
+ };
92
+ }, charge_.Defaults, import("../../server/Transport.js").Http, {}, string | undefined>, ...import("../../Method.js").Server<{
93
+ readonly name: "evm";
94
+ readonly intent: "charge";
95
+ readonly schema: {
96
+ credential: {
97
+ payload: import("zod/mini").ZodMiniObject<{
98
+ from: import("zod/mini").ZodMiniString<string>;
99
+ nonce: import("zod/mini").ZodMiniString<string>;
100
+ signature: import("zod/mini").ZodMiniString<string>;
101
+ to: import("zod/mini").ZodMiniString<string>;
102
+ type: import("zod/mini").ZodMiniLiteral<"authorization">;
103
+ validAfter: import("zod/mini").ZodMiniString<string>;
104
+ validBefore: import("zod/mini").ZodMiniString<string>;
105
+ value: import("zod/mini").ZodMiniString<string>;
106
+ }, import("zod/v4/core").$strip>;
107
+ };
108
+ request: import("zod/mini").ZodMiniPipe<import("zod/mini").ZodMiniObject<{
109
+ amount: import("zod/mini").ZodMiniString<string>;
110
+ chainId: import("zod/mini").ZodMiniNumber<number>;
111
+ currency: import("zod/mini").ZodMiniString<string>;
112
+ credentialTypes: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniEnum<{
113
+ authorization: "authorization";
114
+ }>>>;
115
+ decimals: import("zod/mini").ZodMiniNumber<number>;
116
+ description: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
117
+ externalId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
118
+ permit2Address: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
119
+ recipient: import("zod/mini").ZodMiniString<string>;
120
+ splits: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
121
+ amount: import("zod/mini").ZodMiniString<string>;
122
+ recipient: import("zod/mini").ZodMiniString<string>;
123
+ }, import("zod/v4/core").$strip>>>;
124
+ }, import("zod/v4/core").$strip>, import("zod/mini").ZodMiniTransform<{
125
+ description?: string | undefined;
126
+ externalId?: string | undefined;
127
+ amount: string;
128
+ currency: `0x${string}`;
129
+ methodDetails: {
130
+ chainId: number;
131
+ credentialTypes: string[];
132
+ decimals: number;
133
+ permit2Address?: `0x${string}` | undefined;
134
+ splits?: {
135
+ amount: string;
136
+ recipient: string;
137
+ }[] | undefined;
138
+ };
139
+ recipient: `0x${string}`;
140
+ }, {
141
+ amount: string;
142
+ chainId: number;
143
+ currency: string;
144
+ credentialTypes?: "authorization"[] | undefined;
145
+ decimals: number;
146
+ description?: string | undefined;
147
+ externalId?: string | undefined;
148
+ permit2Address?: string | undefined;
149
+ recipient: string;
150
+ splits?: {
151
+ amount: string;
152
+ recipient: string;
153
+ }[] | undefined;
154
+ }>>;
155
+ };
156
+ }, charge_.Defaults, import("../../server/Transport.js").Http, {}, string | undefined>[]] | readonly [import("../../Method.js").Server<{
7
157
  readonly name: "evm";
8
158
  readonly intent: "charge";
9
159
  readonly schema: {
@@ -69,7 +219,18 @@ export declare function evm<const parameters extends evm.Parameters>(parameters:
69
219
  };
70
220
  }, charge_.Defaults, import("../../server/Transport.js").Http, {}, string | undefined>];
71
221
  export declare namespace evm {
72
- type Parameters = charge_.Parameters;
222
+ /** Accepted EVM currency metadata, including chain-indexed definitions from `viem/tokens`. */
223
+ type Currency = Assets.Currency;
224
+ /** Shared EVM settlement parameters and ordered accepted currencies. */
225
+ type Parameters = Omit<charge_.Parameters, 'currency'> & ({
226
+ /** Ordered accepted currencies. A one-element list accepts a single asset. */
227
+ currencies: readonly Currency[];
228
+ currency?: undefined;
229
+ } | {
230
+ /** @deprecated Use `currencies: [currency]` instead. */
231
+ currency: Currency;
232
+ currencies?: undefined;
233
+ });
73
234
  /** Creates an EVM `charge` server method. */
74
235
  const charge: typeof charge_;
75
236
  /** Known EVM asset metadata for public config. */
@@ -1,9 +1,57 @@
1
1
  import * as Assets from '../Assets.js';
2
2
  import * as Chains from '../Chains.js';
3
3
  import { charge as charge_ } from './Charge.js';
4
- /** Creates EVM server methods from shared charge parameters. */
4
+ /**
5
+ * Creates ordered EVM charge offers from shared parameters.
6
+ *
7
+ * Pass `currencies: [token]` for one asset or an ordered list for multiple assets.
8
+ * Viem token definitions resolve against `chainId`; known assets carry their own
9
+ * network. Unsupported chain entries are skipped and duplicate chain/address
10
+ * pairs are removed. An empty resolved list throws.
11
+ *
12
+ * Token definitions need explicit EIP-3009 domain metadata. Use
13
+ * `evm.assets.fromToken(token, { chainId, transfer })` for per-token domains.
14
+ * A token registry entry alone does not establish EIP-3009 support.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { evm } from 'mppx/server'
19
+ *
20
+ * evm({
21
+ * currencies: [evm.assets.base.USDC],
22
+ * recipient: '0x742d35Cc6634c0532925a3b844bC9e7595F8fE00',
23
+ * settle: async ({ payload }) => settleAuthorization(payload),
24
+ * })
25
+ * ```
26
+ */
5
27
  export function evm(parameters) {
6
- return [evm.charge(parameters)];
28
+ if (parameters.currency !== undefined) {
29
+ if (parameters.currencies !== undefined)
30
+ throw new Error('Specify either `currency` or `currencies`, not both.');
31
+ return [evm.charge(parameters)];
32
+ }
33
+ const { currencies, ...shared } = parameters;
34
+ const seen = new Set();
35
+ const methods = [];
36
+ for (const currency of currencies) {
37
+ const chainId = shared.chainId ?? (Assets.isAsset(currency) ? Assets.toChainId(currency.network) : undefined);
38
+ if (chainId === undefined)
39
+ throw new Error('EVM currencies require `chainId` or known asset metadata.');
40
+ const resolved = Assets.resolve(currency, Assets.toNetwork(chainId));
41
+ if (!resolved)
42
+ continue;
43
+ // One shared price cannot price non-USD or unknown-denomination token definitions.
44
+ if (Assets.isToken(currency) && currency.currency !== 'USD')
45
+ continue;
46
+ const key = `${chainId}:${resolved.address.toLowerCase()}`;
47
+ if (seen.has(key))
48
+ continue;
49
+ seen.add(key);
50
+ methods.push(evm.charge({ ...shared, chainId, currency }));
51
+ }
52
+ if (methods.length === 0)
53
+ throw new Error('No accepted EVM currencies for the configured chain.');
54
+ return methods;
7
55
  }
8
56
  (function (evm) {
9
57
  /** Creates an EVM `charge` server method. */
@@ -1,5 +1,5 @@
1
1
  /** Current mppx package version. */
2
- export declare const version = "0.10.1";
2
+ export declare const version = "0.12.0";
3
3
  /** Canonical SDK identifier for this mppx release. */
4
- export declare const sdkIdentifier = "mppx/0.10.1";
4
+ export declare const sdkIdentifier = "mppx/0.12.0";
5
5
  //# sourceMappingURL=version.d.ts.map
@@ -1,6 +1,6 @@
1
1
  // Generated by scripts/sync-version.ts.
2
2
  /** Current mppx package version. */
3
- export const version = '0.10.1';
3
+ export const version = '0.12.0';
4
4
  /** Canonical SDK identifier for this mppx release. */
5
5
  export const sdkIdentifier = `mppx/${version}`;
6
6
  //# sourceMappingURL=version.js.map
@@ -112,6 +112,7 @@ export type VerifyCredentialOptions = {
112
112
  capturedRequest?: Method.CapturedRequest | undefined;
113
113
  meta?: Record<string, string> | undefined;
114
114
  realm?: string | undefined;
115
+ /** Route input used for binding checks and disambiguating configured methods before schema transforms. */
115
116
  request?: Record<string, unknown> | undefined;
116
117
  /** Optional expected route/resource scope bound via challenge `opaque`. */
117
118
  scope?: string | undefined;
@@ -140,7 +141,8 @@ export type Mppx<methods extends Methods = Methods, transport extends Transport.
140
141
  * all methods through HTTP headers or an MCP payment-required challenge list.
141
142
  *
142
143
  * Each entry is a `[method, options]` tuple where `method` is one of the
143
- * server methods passed to `Mppx.create()`, looked up by `name`+`intent`.
144
+ * server methods passed to `Mppx.create()`, looked up by object identity.
145
+ * String keys use the named handler; function references use their configured method.
144
146
  *
145
147
  * Available on HTTP, MCP JSON-RPC, and MCP SDK transports. MCP handlers
146
148
  * accept the transport's input and return its challenge and receipt types.
@@ -381,6 +383,8 @@ type ConfiguredHandler<transport extends Transport.AnyTransport = Transport.Http
381
383
  type ComposedHandler<transport extends Transport.AnyTransport = Transport.Http> = ((input: Transport.InputOf<transport>) => Promise<MethodFn.Response<transport>>) & {
382
384
  _internal?: {
383
385
  offers: readonly ConfiguredHandler['_internal'][];
386
+ handlers?: readonly ConfiguredHandler<transport>[];
387
+ selectOffers?: SelectOffers<readonly Method.AnyServer[]> | undefined;
384
388
  };
385
389
  };
386
390
  /** An entry for `compose()`: a method reference, handler function ref, or string key paired with its options. */