mppx 0.12.0 → 0.13.1

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 (108) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/Method.d.ts +7 -1
  3. package/dist/Method.js +7 -2
  4. package/dist/Receipt.d.ts +2 -0
  5. package/dist/Receipt.js +2 -0
  6. package/dist/cli/cli.d.ts +8 -0
  7. package/dist/cli/cli.js +134 -136
  8. package/dist/cli/plugins/index.js +12 -14
  9. package/dist/cli/plugins/tempo.js +9 -2
  10. package/dist/cli/utils.d.ts +11 -1
  11. package/dist/cli/utils.js +15 -2
  12. package/dist/cli/validate/challenge.d.ts +2 -0
  13. package/dist/cli/validate/challenge.js +19 -4
  14. package/dist/cli/validate/discovery.d.ts +1 -0
  15. package/dist/cli/validate/discovery.js +10 -1
  16. package/dist/cli/validate/payment.js +34 -12
  17. package/dist/client/Methods.d.ts +0 -2
  18. package/dist/client/Methods.js +0 -2
  19. package/dist/client/Mppx.js +1 -0
  20. package/dist/client/index.d.ts +1 -1
  21. package/dist/client/index.js +1 -1
  22. package/dist/client/internal/Fetch.d.ts +2 -0
  23. package/dist/client/internal/Fetch.js +8 -2
  24. package/dist/client/internal/MethodChallenge.d.ts +1 -0
  25. package/dist/evm/Types.js +1 -1
  26. package/dist/evm/server/Charge.js +5 -0
  27. package/dist/integrations/x402/Evm.js +4 -3
  28. package/dist/internal/HeaderCodec.d.ts +2 -2
  29. package/dist/internal/version.d.ts +2 -2
  30. package/dist/internal/version.js +1 -1
  31. package/dist/mcp/client/McpClient.js +4 -7
  32. package/dist/middlewares/elysia.d.ts +11 -8
  33. package/dist/middlewares/elysia.js +67 -14
  34. package/dist/middlewares/express.js +13 -6
  35. package/dist/middlewares/hono.js +4 -4
  36. package/dist/middlewares/nextjs.js +9 -5
  37. package/dist/proxy/internal/Headers.js +65 -0
  38. package/dist/proxy/services/stripe.js +1 -0
  39. package/dist/server/Mppx.d.ts +13 -1
  40. package/dist/server/Mppx.js +85 -44
  41. package/dist/server/NodeListener.js +71 -6
  42. package/dist/server/Request.d.ts +11 -1
  43. package/dist/server/Request.js +132 -13
  44. package/dist/server/Transport.d.ts +14 -1
  45. package/dist/server/Transport.js +1 -0
  46. package/dist/stripe/server/Methods.d.ts +10 -3
  47. package/dist/stripe/server/Methods.js +17 -2
  48. package/dist/stripe/server/internal/html.gen.js +1 -1
  49. package/dist/tempo/Methods.d.ts +1 -0
  50. package/dist/tempo/Methods.js +1 -0
  51. package/dist/tempo/client/Charge.js +17 -13
  52. package/dist/tempo/client/Methods.d.ts +1 -5
  53. package/dist/tempo/client/Methods.js +10 -5
  54. package/dist/tempo/client/ResolveAccount.d.ts +3 -0
  55. package/dist/tempo/client/index.d.ts +0 -3
  56. package/dist/tempo/client/index.js +0 -2
  57. package/dist/tempo/internal/fee-payer.d.ts +6 -3
  58. package/dist/tempo/internal/fee-payer.js +23 -6
  59. package/dist/tempo/internal/machine-token-charge.d.ts +23 -0
  60. package/dist/tempo/internal/machine-token-charge.js +2 -1
  61. package/dist/tempo/legacy/index.d.ts +1 -3
  62. package/dist/tempo/legacy/index.js +1 -3
  63. package/dist/tempo/server/Charge.js +75 -46
  64. package/dist/tempo/server/Methods.d.ts +55 -10
  65. package/dist/tempo/server/Subscription.d.ts +1 -1
  66. package/dist/tempo/server/Subscription.js +31 -4
  67. package/dist/tempo/server/internal/html.gen.js +1 -1
  68. package/dist/tempo/server/internal/transport.js +28 -11
  69. package/dist/tempo/session/client/ChannelOps.js +5 -0
  70. package/dist/tempo/session/client/Session.d.ts +1 -0
  71. package/dist/tempo/session/client/Session.js +7 -4
  72. package/dist/tempo/session/client/SessionManager.js +3 -0
  73. package/dist/tempo/session/client/Transports.d.ts +8 -1
  74. package/dist/tempo/session/client/Transports.js +12 -3
  75. package/dist/tempo/session/precompile/Chain.js +16 -1
  76. package/dist/tempo/session/precompile/Protocol.d.ts +2 -0
  77. package/dist/tempo/session/precompile/Voucher.d.ts +16 -0
  78. package/dist/tempo/session/precompile/Voucher.js +48 -0
  79. package/dist/tempo/session/server/ChannelStore.d.ts +20 -2
  80. package/dist/tempo/session/server/ChannelStore.js +25 -4
  81. package/dist/tempo/session/server/CredentialVerification.d.ts +4 -1
  82. package/dist/tempo/session/server/CredentialVerification.js +34 -6
  83. package/dist/tempo/session/server/MeteredStream.d.ts +3 -2
  84. package/dist/tempo/session/server/MeteredStream.js +85 -29
  85. package/dist/tempo/session/server/Session.d.ts +2 -1
  86. package/dist/tempo/session/server/Session.js +2 -1
  87. package/dist/tempo/session/server/Settlement.d.ts +21 -0
  88. package/dist/tempo/session/server/Settlement.js +92 -17
  89. package/dist/tempo/session/server/Sse.d.ts +3 -1
  90. package/dist/tempo/session/server/Sse.js +105 -32
  91. package/dist/tempo/session/server/Transports.d.ts +24 -19
  92. package/dist/tempo/session/server/Transports.js +169 -58
  93. package/dist/tempo/session/server/Ws.d.ts +5 -1
  94. package/dist/tempo/session/server/Ws.js +75 -20
  95. package/dist/tempo/subscription/Store.d.ts +2 -0
  96. package/dist/tempo/subscription/Store.js +3 -0
  97. package/dist/validation/core.js +2 -1
  98. package/dist/x402/server/EvmCharge.d.ts +3 -1
  99. package/dist/x402/server/EvmCharge.js +14 -1
  100. package/package.json +6 -6
  101. package/dist/tempo/legacy/client/ChannelOps.d.ts +0 -69
  102. package/dist/tempo/legacy/client/ChannelOps.js +0 -168
  103. package/dist/tempo/legacy/client/Session.d.ts +0 -185
  104. package/dist/tempo/legacy/client/Session.js +0 -279
  105. package/dist/tempo/legacy/client/SessionManager.d.ts +0 -100
  106. package/dist/tempo/legacy/client/SessionManager.js +0 -706
  107. package/dist/tempo/legacy/client/index.d.ts +0 -7
  108. package/dist/tempo/legacy/client/index.js +0 -5
@@ -12,7 +12,7 @@ import * as Constants from '../../Constants.js';
12
12
  import * as Credential from '../../Credential.js';
13
13
  import { tempo as tempoMethods } from '../../tempo/client/index.js';
14
14
  import { tip20ChannelEscrow } from '../../tempo/session/precompile/Protocol.js';
15
- import { signVoucher } from '../../tempo/session/precompile/Voucher.js';
15
+ import { signCloseAuthorization, signVoucher } from '../../tempo/session/precompile/Voucher.js';
16
16
  import { createDefaultStore, createKeychain, resolveAccountName } from '../account.js';
17
17
  import { fetchTokenInfo, fmtBalance, isTempoAccount, isTestnet, link, pc, resolveChain, resolveRpcUrl, } from '../utils.js';
18
18
  import { createPlugin } from './plugin.js';
@@ -227,14 +227,21 @@ export function tempo() {
227
227
  const descriptor = _session?.descriptor;
228
228
  if (!descriptor)
229
229
  throw new Error('session descriptor not available');
230
+ const voucher = { channelId: channelId, cumulativeAmount };
231
+ const signature = await signVoucher(client, account, voucher, escrowContract, chainId);
230
232
  return Credential.serialize({
231
233
  challenge,
232
234
  payload: {
233
235
  action,
234
236
  channelId,
237
+ ...(action === 'close'
238
+ ? {
239
+ closeSignature: await signCloseAuthorization(client, account, voucher, escrowContract, chainId),
240
+ }
241
+ : {}),
235
242
  descriptor,
236
243
  cumulativeAmount: cumulativeAmount.toString(),
237
- signature: await signVoucher(client, account, { channelId: channelId, cumulativeAmount }, escrowContract, chainId),
244
+ signature,
238
245
  },
239
246
  source: `did:pkh:eip155:${chainId}:${account.address}`,
240
247
  });
@@ -96,7 +96,17 @@ export declare const usdc: Address;
96
96
  export declare const mainnetTokens: readonly [`0x${string}`, `0x${string}`];
97
97
  export declare const testnetTokens: readonly ['0x20c0000000000000000000000000000000000000', '0x20c0000000000000000000000000000000000001', '0x20c0000000000000000000000000000000000002', '0x20c0000000000000000000000000000000000003'];
98
98
  export declare function isTestnet(chain: Chain): boolean;
99
- export declare function fetchTokenInfo(client: ReturnType<typeof createClient>, token: Address, account: Address): Promise<{
99
+ /**
100
+ * Fetches a Tempo token balance and display metadata.
101
+ *
102
+ * By default, unavailable decimals fall back to six for display compatibility.
103
+ * Set `requireDecimals` to throw instead when trusted on-chain decimals cannot
104
+ * be resolved.
105
+ */
106
+ export declare function fetchTokenInfo(client: ReturnType<typeof createClient>, token: Address, account: Address, options?: {
107
+ /** Throws instead of using six decimals when token metadata is unavailable. */
108
+ requireDecimals?: boolean;
109
+ }): Promise<{
100
110
  balance: bigint;
101
111
  decimals: number;
102
112
  symbol: string;
package/dist/cli/utils.js CHANGED
@@ -259,14 +259,25 @@ function toBaseUnitAmount(value) {
259
259
  export function isTestnet(chain) {
260
260
  return chain.id !== tempoMainnet.id;
261
261
  }
262
- export async function fetchTokenInfo(client, token, account) {
262
+ /**
263
+ * Fetches a Tempo token balance and display metadata.
264
+ *
265
+ * By default, unavailable decimals fall back to six for display compatibility.
266
+ * Set `requireDecimals` to throw instead when trusted on-chain decimals cannot
267
+ * be resolved.
268
+ */
269
+ export async function fetchTokenInfo(client, token, account, options = {}) {
263
270
  const { Actions } = await import('viem/tempo');
264
271
  const [balance, metadata] = await Promise.all([
265
272
  Actions.token
266
273
  .getBalance(client, { account, token })
267
274
  .then(toBaseUnitAmount)
268
275
  .catch(() => 0n),
269
- Actions.token.getMetadata(client, { token }).catch(() => ({ symbol: token })),
276
+ Actions.token.getMetadata(client, { token }).catch((error) => {
277
+ if (options.requireDecimals)
278
+ throw error;
279
+ return { symbol: token };
280
+ }),
270
281
  ]);
271
282
  const knownSymbols = {
272
283
  [pathUsd]: 'PathUSD',
@@ -274,6 +285,8 @@ export async function fetchTokenInfo(client, token, account) {
274
285
  };
275
286
  const symbol = knownSymbols[token] ?? metadata.symbol;
276
287
  const decimals = 'decimals' in metadata && typeof metadata.decimals === 'number' ? metadata.decimals : 6;
288
+ if (options.requireDecimals && !('decimals' in metadata && typeof metadata.decimals === 'number'))
289
+ throw new Error('Token decimals are unavailable.');
277
290
  return { balance, symbol, decimals, token };
278
291
  }
279
292
  export async function fetchBalanceLines(address, opts) {
@@ -11,6 +11,8 @@ export declare function validateChallenge(baseUrl: string, endpoint: EndpointSpe
11
11
  resolvedBody?: string | undefined;
12
12
  challenges?: Challenge.Challenge[] | undefined;
13
13
  }>;
14
+ /** Validates that challenge realms cover the server hostname. */
15
+ export declare function validateRealmMatchesHost(challenges: Challenge.Challenge[], serverHost: string, results: CheckResult[]): void;
14
16
  export declare function validateErrorHandling(baseUrl: string, endpoint: EndpointSpec, options?: {
15
17
  body?: string | undefined;
16
18
  query?: string[] | undefined;
@@ -15,7 +15,14 @@ function detectTestnet(challenge) {
15
15
  }
16
16
  export async function validateChallenge(baseUrl, endpoint, verbose, options) {
17
17
  const results = [];
18
- const url = buildUrl(baseUrl, endpoint, options?.query);
18
+ let url;
19
+ try {
20
+ url = buildUrl(baseUrl, endpoint, options?.query);
21
+ }
22
+ catch (error) {
23
+ results.push(fail('Request URL valid', error.message));
24
+ return { results };
25
+ }
19
26
  const fetchHeaders = parseHeaders(options?.extraHeaders);
20
27
  let fetchBody;
21
28
  // If the user explicitly provided a body, include it on the first request
@@ -167,11 +174,19 @@ function validateExpiration(challenges, results) {
167
174
  const soonest = Math.min(...challenges.map((ch) => new Date(ch.expires).getTime() - now.getTime()));
168
175
  results.push(check('Challenge expires in the future', `${Math.round(soonest / 60000)}m from now`));
169
176
  }
170
- function validateRealmMatchesHost(challenges, serverHost, results) {
177
+ /** Validates that challenge realms cover the server hostname. */
178
+ export function validateRealmMatchesHost(challenges, serverHost, results) {
179
+ const normalizeHostname = (value) => value.toLowerCase().replace(/\.$/, '');
180
+ const normalizedServerHost = normalizeHostname(serverHost);
171
181
  const realms = [...new Set(challenges.map((ch) => ch.realm ?? ''))];
172
- const badRealms = realms.filter((r) => r && r !== serverHost && !serverHost.endsWith(`.${r}`));
182
+ const badRealms = realms.filter((realm) => {
183
+ const normalizedRealm = normalizeHostname(realm);
184
+ return (!normalizedRealm ||
185
+ (normalizedRealm !== normalizedServerHost &&
186
+ !normalizedServerHost.endsWith(`.${normalizedRealm}`)));
187
+ });
173
188
  if (badRealms.length > 0) {
174
- results.push(warn('Realm matches server hostname', `realm="${badRealms[0]}" vs host="${serverHost}"`, 'Set the realm to your production hostname (or base domain) in the challenge.'));
189
+ results.push(fail('Realm matches server hostname', `realm="${badRealms[0]}" vs host="${serverHost}"`, 'Set the realm to your production hostname (or base domain) in the challenge.'));
175
190
  }
176
191
  else {
177
192
  results.push(check('Realm matches server hostname'));
@@ -9,5 +9,6 @@ export declare function fetchDiscoveryDoc(baseUrl: string): Promise<{
9
9
  export declare function validateLlmsDoc(baseUrl: string): Promise<CheckResult>;
10
10
  export declare function extractEndpointsFromDiscovery(doc: Record<string, unknown>): EndpointSpec[];
11
11
  export declare function extractRequestBodyFromDiscovery(doc: Record<string, unknown>, endpoint: EndpointSpec): string | undefined;
12
+ /** Builds an endpoint URL and rejects paths that could escape the discovery document's origin. */
12
13
  export declare function buildUrl(baseUrl: string, endpoint: EndpointSpec, query?: string[]): string;
13
14
  //# sourceMappingURL=discovery.d.ts.map
@@ -117,14 +117,23 @@ export function extractRequestBodyFromDiscovery(doc, endpoint) {
117
117
  }
118
118
  return undefined;
119
119
  }
120
+ /** Builds an endpoint URL and rejects paths that could escape the discovery document's origin. */
120
121
  export function buildUrl(baseUrl, endpoint, query) {
122
+ if (!endpoint.path.startsWith('/') ||
123
+ endpoint.path.startsWith('//') ||
124
+ endpoint.path.includes('\\'))
125
+ throw new Error(`OpenAPI path must be an origin-relative path: ${endpoint.path}`);
121
126
  let path = endpoint.path;
122
127
  if (endpoint.parameters) {
123
128
  path = substitutePathParams(path, endpoint.parameters);
124
129
  }
125
130
  // Strip leading slash so URL resolves relative to baseUrl's path, not root.
126
131
  const relativePath = path.startsWith('/') ? path.slice(1) : path;
127
- let url = new URL(relativePath, baseUrl.replace(/\/?$/, '/')).href;
132
+ const base = new URL(baseUrl.replace(/\/?$/, '/'));
133
+ const resolved = new URL(relativePath, base);
134
+ if (resolved.origin !== base.origin)
135
+ throw new Error(`OpenAPI path must be an origin-relative path: ${endpoint.path}`);
136
+ let url = resolved.href;
128
137
  if (query) {
129
138
  const u = new URL(url);
130
139
  for (const q of query) {
@@ -15,6 +15,7 @@ import { isTempoSessionChallenge } from '../../tempo/session/client/Transports.j
15
15
  import { resolveAccount, resolveAccountName } from '../account.js';
16
16
  import { assertSamePaymentRequest, flattenConfigMethods, loadConfig, preparePayment, resolvePlugin, } from '../internal.js';
17
17
  import { fetchTokenInfo, confirm, pc } from '../utils.js';
18
+ import { validateRealmMatchesHost } from './challenge.js';
18
19
  import { buildUrl } from './discovery.js';
19
20
  import { check, fail, fetchWithTimeout, formatBytes, isValidIntegerAmount, parseHeaders, skip, warn, } from './helpers.js';
20
21
  async function provisionAndPayTestnet(challenge, verbose, silent) {
@@ -95,16 +96,19 @@ function resolveStripeKey(verbose) {
95
96
  }
96
97
  async function fetchEvmTokenInfo(chain, token, account) {
97
98
  const client = createClient({ chain, transport: http() });
98
- const [balance, symbol] = await Promise.all([
99
- readContract(client, {
100
- address: token,
101
- abi: erc20Abi,
102
- functionName: 'balanceOf',
103
- args: [account],
104
- }),
99
+ const [balance, decimals, symbol] = await Promise.all([
100
+ account
101
+ ? readContract(client, {
102
+ address: token,
103
+ abi: erc20Abi,
104
+ functionName: 'balanceOf',
105
+ args: [account],
106
+ })
107
+ : 0n,
108
+ readContract(client, { address: token, abi: erc20Abi, functionName: 'decimals' }),
105
109
  readContract(client, { address: token, abi: erc20Abi, functionName: 'symbol' }).catch(() => undefined),
106
110
  ]);
107
- return { balance, symbol: symbol ?? undefined };
111
+ return { balance, decimals, symbol: symbol ?? undefined };
108
112
  }
109
113
  function resolveEvmChain(chainId) {
110
114
  const all = Object.values(viemChains);
@@ -162,6 +166,14 @@ export async function validatePaymentFlow(baseUrl, endpoint, verbose, options) {
162
166
  results.push(fail('Payment: parse challenge', 'No Payment challenges in response'));
163
167
  return results;
164
168
  }
169
+ const realmResults = [];
170
+ validateRealmMatchesHost(challenges, new URL(url).hostname, realmResults);
171
+ const realmFailures = realmResults.filter((result) => result.severity === 'fail');
172
+ if (realmFailures.length > 0) {
173
+ results.push(...realmFailures);
174
+ options.onResults?.(realmFailures);
175
+ return results;
176
+ }
165
177
  const loaded = await loadConfig().catch(() => undefined);
166
178
  // Testnet Tempo: always use ephemeral wallet (zero-setup, free money)
167
179
  const tempoTestnetChallenge = challenges.find((ch) => {
@@ -274,7 +286,7 @@ async function attemptCryptoPayment(challenge, tag, ctx) {
274
286
  const requiredAmount = isValidIntegerAmount(request.amount)
275
287
  ? BigInt(request.amount)
276
288
  : undefined;
277
- const decimals = methodDetails?.decimals ?? request.decimals ?? 6;
289
+ let decimals = methodDetails?.decimals ?? request.decimals ?? 6;
278
290
  const currency = request.currency;
279
291
  const { plugin, method: directMethod } = resolvePlugin(challenge, loaded?.config);
280
292
  if (!plugin && !directMethod) {
@@ -306,22 +318,30 @@ async function attemptCryptoPayment(challenge, tag, ctx) {
306
318
  if (chainId)
307
319
  paymentChain = resolveEvmChain(chainId);
308
320
  }
321
+ if (requiredAmount && currency && !paymentChain) {
322
+ results.push(skip(tag, 'unable to resolve payment chain and verify token decimals'));
323
+ return;
324
+ }
309
325
  let tokenSymbol;
310
- if (walletAddress && requiredAmount && currency && paymentChain) {
326
+ if (requiredAmount && currency && paymentChain) {
311
327
  try {
312
328
  let balance;
313
329
  if (challenge.method === Constants.Methods.tempo) {
314
330
  const client = createClient({ chain: paymentChain, transport: http() });
315
- const info = await fetchTokenInfo(client, currency, walletAddress);
331
+ const info = await fetchTokenInfo(client, currency, (walletAddress ?? zeroAddress), {
332
+ requireDecimals: true,
333
+ });
316
334
  balance = info.balance;
335
+ decimals = info.decimals;
317
336
  tokenSymbol = info.symbol;
318
337
  }
319
338
  else {
320
339
  const info = await fetchEvmTokenInfo(paymentChain, currency, walletAddress);
321
340
  balance = info.balance;
341
+ decimals = info.decimals;
322
342
  tokenSymbol = info.symbol;
323
343
  }
324
- if (balance < requiredAmount) {
344
+ if (walletAddress && balance < requiredAmount) {
325
345
  const requiredDisplay = formatAmount(requiredAmount, decimals);
326
346
  const balanceDisplay = formatAmount(balance, decimals);
327
347
  const symbol = tokenSymbol ?? 'tokens';
@@ -332,6 +352,8 @@ async function attemptCryptoPayment(challenge, tag, ctx) {
332
352
  catch (e) {
333
353
  if (verbose)
334
354
  console.log(pc.dim(` Balance check skipped: ${e.message}`));
355
+ results.push(skip(tag, 'unable to verify token balance and decimals from the payment chain'));
356
+ return;
335
357
  }
336
358
  }
337
359
  // Prompt
@@ -4,6 +4,4 @@ export { subscription } from '../tempo/client/Subscription.js';
4
4
  export { session, tempo } from '../tempo/client/index.js';
5
5
  export { session as sessionMethod } from '../tempo/session/client/Session.js';
6
6
  export { sessionManager } from '../tempo/session/client/SessionManager.js';
7
- export { session as sessionLegacy } from '../tempo/legacy/client/index.js';
8
- export { sessionManager as sessionLegacyManager } from '../tempo/legacy/client/index.js';
9
7
  //# sourceMappingURL=Methods.d.ts.map
@@ -4,6 +4,4 @@ export { subscription } from '../tempo/client/Subscription.js';
4
4
  export { session, tempo } from '../tempo/client/index.js';
5
5
  export { session as sessionMethod } from '../tempo/session/client/Session.js';
6
6
  export { sessionManager } from '../tempo/session/client/SessionManager.js';
7
- export { session as sessionLegacy } from '../tempo/legacy/client/index.js';
8
- export { sessionManager as sessionLegacyManager } from '../tempo/legacy/client/index.js';
9
7
  //# sourceMappingURL=Methods.js.map
@@ -181,6 +181,7 @@ export function create(config) {
181
181
  context,
182
182
  fetch: attestedFetch,
183
183
  input: preparedHttp.replayRequest,
184
+ signal: preparedHttp.replayRequest.signal,
184
185
  });
185
186
  return payment.createCredential(context);
186
187
  });
@@ -1,7 +1,7 @@
1
1
  export * as Constants from '../Constants.js';
2
2
  export * as Expires from '../Expires.js';
3
3
  export * as Fetch from './internal/Fetch.js';
4
- export { evm, session, sessionManager, sessionMethod, sessionLegacy, sessionLegacyManager, stripe, tempo, } from './Methods.js';
4
+ export { evm, session, sessionManager, sessionMethod, stripe, tempo } from './Methods.js';
5
5
  export { createChannelStore, createJsonChannelStore, entryKey, type ChannelStore, type JsonChannelKv, } from '../tempo/session/client/ChannelStore.js';
6
6
  export type { ChargeContext } from '../tempo/client/Charge.js';
7
7
  export type { ResolveAccount, ResolveAccountCall, ResolveAccountInfo, ResolveAccountOperation, } from '../tempo/client/ResolveAccount.js';
@@ -1,7 +1,7 @@
1
1
  export * as Constants from '../Constants.js';
2
2
  export * as Expires from '../Expires.js';
3
3
  export * as Fetch from './internal/Fetch.js';
4
- export { evm, session, sessionManager, sessionMethod, sessionLegacy, sessionLegacyManager, stripe, tempo, } from './Methods.js';
4
+ export { evm, session, sessionManager, sessionMethod, stripe, tempo } from './Methods.js';
5
5
  export { createChannelStore, createJsonChannelStore, entryKey, } from '../tempo/session/client/ChannelStore.js';
6
6
  export * as Mppx from './Mppx.js';
7
7
  export * as Transport from './Transport.js';
@@ -171,6 +171,8 @@ export declare function createEventDispatcher<methods extends readonly Method.An
171
171
  /** @internal */
172
172
  export declare function unwrapFetch(fetch: typeof globalThis.fetch): typeof globalThis.fetch;
173
173
  /** @internal */
174
+ export declare function resolveRequestSignal(input: RequestInfo | URL, init: RequestInit | undefined): AbortSignal | undefined;
175
+ /** @internal */
174
176
  export declare function validateCredentialHeaderValue(credential: string): void;
175
177
  /** @internal */
176
178
  /** @internal */
@@ -122,6 +122,7 @@ export function from(config) {
122
122
  context: credentialContext,
123
123
  fetch: baseFetch,
124
124
  input: paymentInput,
125
+ signal: resolveRequestSignal(input, init),
125
126
  }),
126
127
  };
127
128
  const promise = attempt
@@ -576,8 +577,13 @@ function cloneRequestInput(input) {
576
577
  return input;
577
578
  }
578
579
  }
579
- function resolveRequestSignal(input, init) {
580
- return init?.signal ?? (input instanceof Request ? input.signal : undefined);
580
+ /** @internal */
581
+ export function resolveRequestSignal(input, init) {
582
+ if (init?.signal === null)
583
+ return undefined;
584
+ if (init?.signal !== undefined)
585
+ return init.signal;
586
+ return input instanceof Request ? input.signal : undefined;
581
587
  }
582
588
  /** @internal */
583
589
  function isWrappedFetch(fetch) {
@@ -7,6 +7,7 @@ export type HandlerParameters = {
7
7
  context?: unknown;
8
8
  fetch: typeof globalThis.fetch;
9
9
  input: RequestInfo | URL;
10
+ signal?: AbortSignal | undefined;
10
11
  };
11
12
  /** Internal client-method challenge hook. */
12
13
  export type Handler = (parameters: HandlerParameters) => MaybePromise<void>;
package/dist/evm/Types.js CHANGED
@@ -87,7 +87,7 @@ export function authorizationDomain(parameters) {
87
87
  }
88
88
  /** Computes the Payment-auth EVM challenge hash used as the EIP-3009 nonce. */
89
89
  export function challengeHash(challenge) {
90
- return Hash.keccak256(Bytes.fromString(`${challenge.id}${challenge.realm}`), {
90
+ return Hash.keccak256(Bytes.fromString(JSON.stringify([challenge.id, challenge.realm])), {
91
91
  as: 'Hex',
92
92
  });
93
93
  }
@@ -171,6 +171,7 @@ function createPaths(config) {
171
171
  function createMppPath() {
172
172
  const transport = ServerTransport.http();
173
173
  return {
174
+ credentialHeaders: transport.credentialHeaders ?? [],
174
175
  bindCredential: (options) => transport.bindCredential?.(options) ?? options.credential,
175
176
  captureRequest: transport.captureRequest,
176
177
  getCredential: transport.getCredential,
@@ -181,10 +182,14 @@ function createMppPath() {
181
182
  function httpTransport(paths) {
182
183
  return ServerTransport.from({
183
184
  name: 'evm-http',
185
+ credentialHeaders: [
186
+ ...new Set([...paths.mpp.credentialHeaders, ...paths.x402.credentialHeaders]),
187
+ ],
184
188
  captureRequest: paths.mpp.captureRequest,
185
189
  getCredential(input) {
186
190
  return paths.mpp.getCredential(input) ?? paths.x402.getCredential(input);
187
191
  },
192
+ matchCredential: paths.x402.matchCredential,
188
193
  bindCredential(options) {
189
194
  if (X402.isPendingCredential(options.credential))
190
195
  return paths.x402.bindCredential(options);
@@ -4,7 +4,6 @@ import { evm } from '../../evm/server/Methods.js';
4
4
  import { eip3009 } from '../../evm/Types.js';
5
5
  import * as Expires from '../../Expires.js';
6
6
  import * as Mppx from '../../server/Mppx.js';
7
- import { tempo } from '../../tempo/server/Methods.js';
8
7
  import * as Types from '../../x402/Types.js';
9
8
  /** Creates a native MPP handler backed by a compatible x402 EVM requirement. */
10
9
  export function createHandler(parameters) {
@@ -37,8 +36,10 @@ export function createHandler(parameters) {
37
36
  return { reference: result.reference };
38
37
  },
39
38
  });
39
+ // Only advertise a native rail whose credential can be verified and settled
40
+ // by the selected x402 scheme. A Tempo transaction is not an EIP-3009 payload.
40
41
  const payment = Mppx.create({
41
- methods: [tempo.charge({ recipient: normalizeAddress(requirement.payTo) }), evmMethod],
42
+ methods: [evmMethod],
42
43
  ...(parameters.config.realm ? { realm: parameters.config.realm } : {}),
43
44
  secretKey: parameters.config.secretKey,
44
45
  });
@@ -50,7 +51,7 @@ export function createHandler(parameters) {
50
51
  expires: Expires.seconds(requirement.maxTimeoutSeconds),
51
52
  scope: parameters.paymentRequired.resource.url,
52
53
  };
53
- const handler = payment.compose([payment.tempo.charge, options], [payment.evm.charge, options]);
54
+ const handler = payment.compose([payment.evm.charge, options]);
54
55
  return async (request) => ({
55
56
  payment: await handler(request),
56
57
  skipHandlerResponse,
@@ -7,8 +7,8 @@ import type * as z from '../zod.js';
7
7
  * can opt into the exact wire encoding their protocol expects.
8
8
  */
9
9
  export declare function createJson<const schema extends z.ZodMiniType>(schema: schema): {
10
- encode(value: z.infer<schema>): string;
11
- decode(value: string): z.infer<schema>;
10
+ encode(value: z.output<schema>): string;
11
+ decode(value: string): z.output<schema>;
12
12
  };
13
13
  /** Error thrown when a JSON header value is not valid base64-encoded JSON. */
14
14
  export declare class InvalidJsonHeaderError extends Error {
@@ -1,5 +1,5 @@
1
1
  /** Current mppx package version. */
2
- export declare const version = "0.12.0";
2
+ export declare const version = "0.13.1";
3
3
  /** Canonical SDK identifier for this mppx release. */
4
- export declare const sdkIdentifier = "mppx/0.12.0";
4
+ export declare const sdkIdentifier = "mppx/0.13.1";
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.12.0';
3
+ export const version = '0.13.1';
4
4
  /** Canonical SDK identifier for this mppx release. */
5
5
  export const sdkIdentifier = `mppx/${version}`;
6
6
  //# sourceMappingURL=version.js.map
@@ -88,14 +88,11 @@ export function isPaymentRequiredError(error) {
88
88
  }
89
89
  /** @internal */
90
90
  async function createCredential(challenge, config) {
91
- const { context, methods } = config;
92
- const mi = methods.find((m) => m.name === challenge.method && m.intent === challenge.intent);
93
- if (!mi)
94
- throw new Error(`No method found for "${challenge.method}.${challenge.intent}". Available: ${methods.map((m) => `${m.name}.${m.intent}`).join(', ')}`);
91
+ const { context, method } = config;
95
92
  if (challenge.expires)
96
93
  Expires.assert(challenge.expires, challenge.id);
97
- const parsedContext = mi.context && context !== undefined ? mi.context.parse(context) : undefined;
98
- return mi.createCredential(parsedContext !== undefined ? { challenge, context: parsedContext } : { challenge });
94
+ const parsedContext = method.context && context !== undefined ? method.context.parse(context) : undefined;
95
+ return method.createCredential(parsedContext !== undefined ? { challenge, context: parsedContext } : { challenge });
99
96
  }
100
97
  function createPaymentAwareCallTool(callTool, config) {
101
98
  const methods = config.methods.flat();
@@ -121,7 +118,7 @@ function createPaymentAwareCallTool(callTool, config) {
121
118
  }
122
119
  const credential = await createCredential(selected.challenge, {
123
120
  context: call.context,
124
- methods,
121
+ method: selected.method,
125
122
  });
126
123
  const parsed = Credential.deserialize(credential);
127
124
  try {
@@ -1,16 +1,19 @@
1
- import { Elysia, type Context } from 'elysia';
1
+ import { Elysia, type AfterHandler, type OptionalHandler } from 'elysia';
2
2
  import { type GenerateConfig, type RouteConfig } from '../discovery/OpenApi.js';
3
3
  import * as Mppx_core from '../server/Mppx.js';
4
4
  import * as Mppx_internal from './internal/mppx.js';
5
5
  export * from '../server/Methods.js';
6
- type ElysiaHook = (context: Context) => Promise<Response | undefined>;
6
+ type ElysiaHook = OptionalHandler & {
7
+ afterHandle: AfterHandler;
8
+ beforeHandle: OptionalHandler;
9
+ };
7
10
  export declare namespace Mppx {
8
11
  /**
9
12
  * Creates an Elysia-aware payment handler where each intent
10
- * returns an Elysia `beforeHandle` hook.
13
+ * returns Elysia lifecycle hooks.
11
14
  *
12
- * Use with `.guard()` to scope payment to specific routes,
13
- * or `.onBeforeHandle()` to apply globally.
15
+ * Use with `.guard()` so both verification and response wrapping are scoped
16
+ * to the same routes.
14
17
  *
15
18
  * @example
16
19
  * ```ts
@@ -21,7 +24,7 @@ export declare namespace Mppx {
21
24
  *
22
25
  * const app = new Elysia()
23
26
  * .guard(
24
- * { beforeHandle: mppx.charge({ amount: '1' }) },
27
+ * mppx.charge({ amount: '1' }),
25
28
  * (app) => app.get('/premium', () => ({ data: 'paid content' })),
26
29
  * )
27
30
  * ```
@@ -29,7 +32,7 @@ export declare namespace Mppx {
29
32
  function create<const methods extends Mppx_core.Methods>(config: Mppx_core.create.Config<methods>): Mppx_internal.Wrap<Mppx_core.Mppx<methods>, ElysiaHook>;
30
33
  }
31
34
  /**
32
- * Elysia `beforeHandle` hook that gates a route behind a payment intent.
35
+ * Elysia lifecycle hooks that gate a route behind a payment intent.
33
36
  *
34
37
  * Returns a 402 challenge if no valid credential is provided.
35
38
  *
@@ -43,7 +46,7 @@ export declare namespace Mppx {
43
46
  *
44
47
  * const app = new Elysia()
45
48
  * .guard(
46
- * { beforeHandle: payment(mppx.charge, { amount: '1' }) },
49
+ * payment(mppx.charge, { amount: '1' }),
47
50
  * (app) => app.get('/premium', () => ({ data: 'paid content' })),
48
51
  * )
49
52
  * ```