@nevermined-io/payments 1.11.2 → 1.13.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 +33 -0
- package/dist/a2a/clientRegistry.d.ts +10 -1
- package/dist/a2a/clientRegistry.d.ts.map +1 -1
- package/dist/a2a/clientRegistry.js +13 -4
- package/dist/a2a/clientRegistry.js.map +1 -1
- package/dist/a2a/paymentsClient.d.ts +60 -3
- package/dist/a2a/paymentsClient.d.ts.map +1 -1
- package/dist/a2a/paymentsClient.js +129 -8
- package/dist/a2a/paymentsClient.js.map +1 -1
- package/dist/a2a/types.d.ts +5 -0
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/a2a/types.js.map +1 -1
- package/dist/api/nvm-api.d.ts +2 -0
- package/dist/api/nvm-api.d.ts.map +1 -1
- package/dist/api/nvm-api.js +4 -0
- package/dist/api/nvm-api.js.map +1 -1
- package/dist/api/orders-api.d.ts +146 -0
- package/dist/api/orders-api.d.ts.map +1 -0
- package/dist/api/orders-api.js +107 -0
- package/dist/api/orders-api.js.map +1 -0
- package/dist/common/types.d.ts +87 -0
- package/dist/common/types.d.ts.map +1 -1
- package/dist/common/types.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/core/paywall.d.ts.map +1 -1
- package/dist/mcp/core/paywall.js +10 -0
- package/dist/mcp/core/paywall.js.map +1 -1
- package/dist/mpp/fetch.d.ts +8 -3
- package/dist/mpp/fetch.d.ts.map +1 -1
- package/dist/mpp/fetch.js +6 -0
- package/dist/mpp/fetch.js.map +1 -1
- package/dist/mpp/mpp-api.d.ts +11 -5
- package/dist/mpp/mpp-api.d.ts.map +1 -1
- package/dist/mpp/mpp-api.js +10 -3
- package/dist/mpp/mpp-api.js.map +1 -1
- package/dist/payments.d.ts +3 -0
- package/dist/payments.d.ts.map +1 -1
- package/dist/payments.js +4 -0
- package/dist/payments.js.map +1 -1
- package/dist/x402/express/middleware.d.ts.map +1 -1
- package/dist/x402/express/middleware.js +98 -1
- package/dist/x402/express/middleware.js.map +1 -1
- package/dist/x402/facilitator-api.d.ts +90 -7
- package/dist/x402/facilitator-api.d.ts.map +1 -1
- package/dist/x402/facilitator-api.js +64 -3
- package/dist/x402/facilitator-api.js.map +1 -1
- package/dist/x402/index.d.ts +3 -2
- package/dist/x402/index.d.ts.map +1 -1
- package/dist/x402/index.js +1 -0
- package/dist/x402/index.js.map +1 -1
- package/dist/x402/token-request.d.ts +8 -0
- package/dist/x402/token-request.d.ts.map +1 -1
- package/dist/x402/token-request.js +101 -1
- package/dist/x402/token-request.js.map +1 -1
- package/dist/x402/token-version.d.ts +97 -0
- package/dist/x402/token-version.d.ts.map +1 -0
- package/dist/x402/token-version.js +127 -0
- package/dist/x402/token-version.js.map +1 -0
- package/dist/x402/token.d.ts +33 -2
- package/dist/x402/token.d.ts.map +1 -1
- package/dist/x402/token.js +47 -2
- package/dist/x402/token.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paywall.js","sourceRoot":"","sources":["../../../src/mcp/core/paywall.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Main paywall decorator for MCP handlers (tools, resources, prompts)\n */\nimport { Address, isValidScheme } from '../../common/types.js'\nimport type { Payments } from '../../payments.js'\nimport { decodeAccessToken, encodeAccessToken } from '../../utils.js'\nimport {\n buildPaymentRequired,\n buildPaymentRequiredForPlans,\n type SettlePermissionsResult,\n type X402PaymentRequired,\n} from '../../x402/facilitator-api.js'\nimport {\n AuthResult,\n McpConfig,\n PaywallOptions,\n PromptOptions,\n ResourceOptions,\n ToolOptions,\n} from '../types/paywall.types.js'\nimport {\n ERROR_CODES,\n PaymentRequiredError,\n SettlementFailedError,\n createRpcError,\n} from '../utils/errors.js'\nimport {\n NEVERMINED_CREDITS_META_KEY,\n X402_PAYMENT_RESPONSE_META_KEY,\n paymentRequiredResult,\n readPaymentPayload,\n} from '../utils/meta.js'\nimport { PaywallAuthenticator } from './auth.js'\nimport { CreditsContextProvider } from './credits-context.js'\n\n// Emit the Authorization-header deprecation notice at most once per process to\n// avoid log spam on high-traffic servers still using the legacy header path.\nlet authHeaderDeprecationWarned = false\n\n/**\n * Main class for creating paywall-protected MCP handlers\n */\nexport class PaywallDecorator {\n // Internal config ensures serverName is always a concrete string\n private config: { planId: string; agentId?: string; serverName: string } = {\n planId: '',\n serverName: 'mcp-server',\n }\n\n constructor(\n private payments: Payments,\n private authenticator: PaywallAuthenticator,\n private creditsContext: CreditsContextProvider,\n ) {}\n\n /**\n * Configure the paywall with agent and server information\n */\n configure(options: McpConfig): void {\n this.config = {\n planId: options.planId || this.config.planId,\n agentId: options.agentId ?? this.config.agentId,\n serverName: options.serverName ?? this.config.serverName,\n }\n }\n\n /**\n * Create a paywall-protected handler (uncurried version only)\n */\n // Overloads per kind for stronger typing\n protect<TArgs = any>(\n handler: (args: TArgs, extra?: any) => Promise<any> | any,\n options: ToolOptions | PromptOptions,\n ): (args: TArgs, extra?: any) => Promise<any>\n protect(\n handler: (\n uri: URL,\n variables: Record<string, string | string[]>,\n extra?: any,\n ) => Promise<any> | any,\n options: ResourceOptions,\n ): (uri: URL, variables: Record<string, string | string[]>, extra?: any) => Promise<any>\n protect(handler: any, options: PaywallOptions): any {\n return this.createWrappedHandler(handler, options)\n }\n\n /**\n * Internal method to create the wrapped handler\n */\n private createWrappedHandler<TArgs = any>(\n handler: (args: TArgs, extra?: any) => Promise<any> | any,\n options: PaywallOptions,\n ): (...allArgs: any[]) => Promise<any> {\n return async (...allArgs: any[]): Promise<any> => {\n // Validate configuration: a planId must be resolvable (per-tool option or\n // server-level config). agentId is optional under the plan-centric model\n // (the facilitator resolves everything from planId + token).\n const configuredPlanId = options?.planId ?? this.config.planId\n if (!configuredPlanId) {\n throw createRpcError(\n ERROR_CODES.Misconfiguration,\n 'Server misconfiguration: missing planId',\n )\n }\n\n const kind = options?.kind ?? 'tool'\n const name = options?.name ?? 'unnamed'\n\n // Detect resource signature: (url, variables, extra)\n const isResource = allArgs.length >= 2 && allArgs[0] instanceof URL\n const extra = isResource ? allArgs[2] : allArgs[1]\n const argsOrVars = isResource ? allArgs[1] : allArgs[0]\n\n try {\n // x402 v2 MCP transport: prefer the in-band payment payload from\n // params._meta[\"x402/payment\"]. Re-encode it into the access token\n // string the verify/settle path expects and present it via the same\n // extra/headers shape the auth flow reads, so the in-band payload takes\n // precedence over the Authorization header (kept as a deprecated\n // fallback when the in-band payload is absent). The RAW extra is still\n // forwarded to the user handler below.\n const paymentPayload = readPaymentPayload(extra)\n let authExtra = extra\n if (paymentPayload) {\n const token = encodeAccessToken(paymentPayload)\n // Synthesize an auth-only extra carrying the in-band token. This\n // intentionally drops the rest of `extra` for the AUTH call only; the\n // RAW `extra` (with `_meta`) is still forwarded to the user handler below.\n authExtra = { requestInfo: { headers: { authorization: `Bearer ${token}` } } }\n } else if (!authHeaderDeprecationWarned) {\n authHeaderDeprecationWarned = true\n console.warn(\n '[x402] No _meta[\"x402/payment\"] on the MCP request; falling back to the ' +\n 'Authorization header (deprecated under the x402 v2 MCP transport).',\n )\n }\n\n // 1. Authenticate request\n const authResult = await this.authenticator.authenticate(\n authExtra,\n { planId: configuredPlanId, maxAmount: options?.maxAmount },\n this.config.agentId,\n this.config.serverName,\n name,\n kind,\n argsOrVars,\n )\n\n // 2. Pre-calculate credits if they are fixed (not a function)\n // This allows handlers to access credits during execution\n const creditsOption = options?.credits\n const isFixedCredits = typeof creditsOption === 'bigint' || creditsOption === undefined\n const preCalculatedCredits = isFixedCredits\n ? this.creditsContext.resolve(creditsOption, argsOrVars, null, authResult)\n : undefined\n\n // Determine effective planId: explicit option overrides token-derived value\n const effectivePlanId = options?.planId ?? authResult.planId\n\n // 3. Build PaywallContext for handler (with extra wrapper for backward compatibility)\n const paywallContext = {\n authResult,\n credits: preCalculatedCredits,\n planId: authResult.planId,\n subscriberAddress: authResult.subscriberAddress,\n agentRequest: authResult.agentRequest,\n }\n\n // 4. Execute original handler with context\n const result = await (handler as any)(...allArgs, paywallContext)\n\n // 5. Resolve final credits to burn (may be different if credits are dynamic)\n const credits = isFixedCredits\n ? (preCalculatedCredits ?? 1n)\n : this.creditsContext.resolve(creditsOption, argsOrVars, result, authResult)\n\n // Update context with final resolved credits\n paywallContext.credits = credits\n\n // 6. If the result is an AsyncIterable (stream), redeem on completion\n if (isAsyncIterable(result)) {\n const onFinally = async () => {\n return await this.redeemCredits(\n effectivePlanId,\n authResult.token,\n authResult.subscriberAddress,\n credits,\n options,\n authResult.agentId,\n authResult.logicalUrl,\n authResult.httpUrl,\n 'POST',\n )\n }\n return wrapAsyncIterable(\n result,\n onFinally,\n effectivePlanId,\n authResult.subscriberAddress,\n credits,\n )\n }\n\n // 7. Non-streaming: redeem immediately\n const creditsResult = await this.redeemCredits(\n effectivePlanId,\n authResult.token,\n authResult.subscriberAddress,\n credits,\n options,\n authResult.agentId,\n authResult.logicalUrl,\n // fix: pre-existing arg order — fallbackEndpoint=httpUrl, httpVerb='POST'\n // (matches the streaming site above)\n authResult.httpUrl,\n 'POST',\n )\n\n // Settlement failed AFTER the tool executed: per the x402 v2 MCP\n // transport spec, do NOT return the tool's content — surface only the\n // payment error so a paid result is never delivered without payment\n // landing. (onRedeemError \"ignore\" therefore no longer delivers paid\n // content; \"propagate\" already threw a Misconfiguration in redeemCredits.)\n if (creditsResult && !creditsResult.success) {\n console.error(\n `[x402] settlement failed after tool execution; suppressing tool content. reason=${creditsResult.errorReason}`,\n )\n throw new SettlementFailedError(this.buildPaymentRequiredFromAuth(authResult))\n }\n\n // creditsResult is undefined for free / no-credit calls (no settlement\n // performed) — in that case the spec receipt is omitted. On success the\n // full receipt goes under the spec key; Nevermined observability is kept\n // under a namespaced key so it never collides with the spec shape.\n result._meta = {\n ...result._meta,\n ...(creditsResult && { [X402_PAYMENT_RESPONSE_META_KEY]: creditsResult }),\n [NEVERMINED_CREDITS_META_KEY]: {\n ...(creditsResult?.transaction && { txHash: creditsResult.transaction }),\n creditsRedeemed: creditsResult?.success\n ? (creditsResult.creditsRedeemed ?? credits.toString())\n : '0',\n remainingBalance: creditsResult?.remainingBalance,\n planId: authResult.planId,\n subscriberAddress: authResult.subscriberAddress,\n success: creditsResult ? creditsResult.success : true,\n },\n }\n return result\n } catch (error) {\n // Payment-required (pre-execution, from auth) and settlement-failure\n // (post-execution) are surfaced in band as an error tool result for\n // tools. Resources/prompts have no tool-result error channel, so the\n // error propagates as a JSON-RPC error instead.\n if (error instanceof PaymentRequiredError && kind === 'tool') {\n return paymentRequiredResult(error.paymentRequired)\n }\n throw error\n }\n }\n }\n\n /**\n * Build a spec-shaped `PaymentRequired` dict for a settlement failure, from\n * the authenticated request context. Surfaced (with tool content suppressed)\n * when settlement fails after the tool has executed.\n */\n private buildPaymentRequiredFromAuth(authResult: AuthResult): Record<string, any> {\n const planId = authResult.planId || ''\n const paymentRequired = buildPaymentRequiredForPlans(planId ? [planId] : [''], {\n endpoint: authResult.logicalUrl || authResult.httpUrl,\n agentId: authResult.agentId,\n httpVerb: 'POST',\n environment: this.payments.getEnvironmentName(),\n }) as X402PaymentRequired & { error?: string }\n paymentRequired.error = 'settlement failed'\n return paymentRequired\n }\n\n /**\n * Redeem credits after successful request\n */\n private async redeemCredits(\n planId: string,\n token: string,\n subscriberAddress: Address,\n credits: bigint,\n options: PaywallOptions,\n agentId?: string,\n endpoint?: string,\n fallbackEndpoint?: string,\n httpVerb?: string,\n ): Promise<SettlePermissionsResult | undefined> {\n // No settlement for free / no-credit calls — signalled to the caller as\n // `undefined` so the spec receipt (_meta[\"x402/payment-response\"]) is omitted.\n if (!(credits && credits > 0n && subscriberAddress && planId)) {\n return undefined\n }\n\n const decoded = decodeAccessToken(token)\n const scheme = isValidScheme(decoded?.accepted?.scheme)\n ? decoded.accepted.scheme\n : 'nvm:erc4337'\n try {\n const paymentRequired: X402PaymentRequired = buildPaymentRequired(planId, {\n endpoint: endpoint || '',\n agentId,\n httpVerb,\n scheme,\n environment: this.payments.getEnvironmentName(),\n })\n\n return await this.payments.facilitator.settlePermissions({\n paymentRequired,\n x402AccessToken: token,\n maxAmount: credits,\n })\n } catch (primaryError) {\n // If logical URL fails and we have an HTTP URL fallback, retry with it\n let lastError: unknown = primaryError\n if (fallbackEndpoint) {\n try {\n const paymentRequired: X402PaymentRequired = buildPaymentRequired(planId, {\n endpoint: fallbackEndpoint,\n agentId,\n httpVerb,\n scheme,\n environment: this.payments.getEnvironmentName(),\n })\n\n return await this.payments.facilitator.settlePermissions({\n paymentRequired,\n x402AccessToken: token,\n maxAmount: credits,\n })\n } catch (fallbackError) {\n // Fallback also failed, use fallback error as the reported error\n lastError = fallbackError\n }\n }\n\n const errorReason = lastError instanceof Error ? lastError.message : String(lastError)\n console.error(`[x402] settle failed: ${errorReason}`)\n if (options.onRedeemError === 'propagate') {\n throw createRpcError(\n ERROR_CODES.Misconfiguration,\n `Failed to redeem credits: ${errorReason}`,\n )\n }\n // Default (\"ignore\"): return a failed result so the caller suppresses the\n // tool content and surfaces the in-band payment error (always-suppress\n // under the x402 v2 MCP transport).\n return { success: false, transaction: '', network: '', errorReason }\n }\n }\n}\n\n/**\n * Type guard to detect AsyncIterable values.\n */\nfunction isAsyncIterable<T = unknown>(value: any): value is AsyncIterable<T> {\n return value != null && typeof value[Symbol.asyncIterator] === 'function'\n}\n\n/**\n * Wrap an AsyncIterable with metadata injection at the end of the stream\n */\nfunction wrapAsyncIterable<T>(\n iterable: AsyncIterable<T>,\n onFinally: () => Promise<any>,\n planId: string,\n subscriberAddress: Address,\n credits: bigint,\n) {\n async function* generator() {\n let creditsResult: any = null\n try {\n for await (const chunk of iterable) {\n yield chunk as T\n }\n } finally {\n creditsResult = await onFinally()\n }\n\n // Yield a _meta chunk at the end with the redemption result.\n // NOTE: a stream cannot retroactively suppress already-yielded chunks, so a\n // post-execution settlement failure on a stream is only reported here in the\n // final _meta chunk (under nevermined/credits) — it cannot withhold content\n // the way a non-streaming tool result does. `creditsResult` is undefined for\n // free / no-credit calls.\n const settlement = creditsResult || undefined\n const metadataChunk = {\n _meta: {\n // Spec receipt only on a successful settlement.\n ...(settlement?.success && { [X402_PAYMENT_RESPONSE_META_KEY]: settlement }),\n // Nevermined-namespaced observability (NOT part of the x402 spec).\n [NEVERMINED_CREDITS_META_KEY]: {\n ...(settlement?.transaction && { txHash: settlement.transaction }),\n creditsRedeemed: settlement?.success\n ? (settlement.creditsRedeemed ?? credits.toString())\n : '0',\n remainingBalance: settlement?.remainingBalance,\n planId,\n subscriberAddress,\n success: settlement ? settlement.success : true,\n ...(settlement?.errorReason && { errorReason: settlement.errorReason }),\n },\n },\n }\n yield metadataChunk as T\n }\n return generator()\n}\n"]}
|
|
1
|
+
{"version":3,"file":"paywall.js","sourceRoot":"","sources":["../../../src/mcp/core/paywall.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Main paywall decorator for MCP handlers (tools, resources, prompts)\n */\nimport { Address, isValidScheme } from '../../common/types.js'\nimport type { Payments } from '../../payments.js'\nimport { decodeAccessToken, encodeAccessToken } from '../../utils.js'\nimport {\n buildPaymentRequired,\n buildPaymentRequiredForPlans,\n type SettlePermissionsResult,\n type X402PaymentRequired,\n} from '../../x402/facilitator-api.js'\nimport {\n AuthResult,\n McpConfig,\n PaywallOptions,\n PromptOptions,\n ResourceOptions,\n ToolOptions,\n} from '../types/paywall.types.js'\nimport {\n ERROR_CODES,\n PaymentRequiredError,\n SettlementFailedError,\n createRpcError,\n} from '../utils/errors.js'\nimport {\n NEVERMINED_CREDITS_META_KEY,\n X402_PAYMENT_RESPONSE_META_KEY,\n paymentRequiredResult,\n readPaymentPayload,\n} from '../utils/meta.js'\nimport { PaywallAuthenticator } from './auth.js'\nimport { CreditsContextProvider } from './credits-context.js'\n\n// Emit the Authorization-header deprecation notice at most once per process to\n// avoid log spam on high-traffic servers still using the legacy header path.\nlet authHeaderDeprecationWarned = false\n\n/**\n * Main class for creating paywall-protected MCP handlers\n */\nexport class PaywallDecorator {\n // Internal config ensures serverName is always a concrete string\n private config: { planId: string; agentId?: string; serverName: string } = {\n planId: '',\n serverName: 'mcp-server',\n }\n\n constructor(\n private payments: Payments,\n private authenticator: PaywallAuthenticator,\n private creditsContext: CreditsContextProvider,\n ) {}\n\n /**\n * Configure the paywall with agent and server information\n */\n configure(options: McpConfig): void {\n this.config = {\n planId: options.planId || this.config.planId,\n agentId: options.agentId ?? this.config.agentId,\n serverName: options.serverName ?? this.config.serverName,\n }\n }\n\n /**\n * Create a paywall-protected handler (uncurried version only)\n */\n // Overloads per kind for stronger typing\n protect<TArgs = any>(\n handler: (args: TArgs, extra?: any) => Promise<any> | any,\n options: ToolOptions | PromptOptions,\n ): (args: TArgs, extra?: any) => Promise<any>\n protect(\n handler: (\n uri: URL,\n variables: Record<string, string | string[]>,\n extra?: any,\n ) => Promise<any> | any,\n options: ResourceOptions,\n ): (uri: URL, variables: Record<string, string | string[]>, extra?: any) => Promise<any>\n protect(handler: any, options: PaywallOptions): any {\n return this.createWrappedHandler(handler, options)\n }\n\n /**\n * Internal method to create the wrapped handler\n */\n private createWrappedHandler<TArgs = any>(\n handler: (args: TArgs, extra?: any) => Promise<any> | any,\n options: PaywallOptions,\n ): (...allArgs: any[]) => Promise<any> {\n return async (...allArgs: any[]): Promise<any> => {\n // Validate configuration: a planId must be resolvable (per-tool option or\n // server-level config). agentId is optional under the plan-centric model\n // (the facilitator resolves everything from planId + token).\n const configuredPlanId = options?.planId ?? this.config.planId\n if (!configuredPlanId) {\n throw createRpcError(\n ERROR_CODES.Misconfiguration,\n 'Server misconfiguration: missing planId',\n )\n }\n\n const kind = options?.kind ?? 'tool'\n const name = options?.name ?? 'unnamed'\n\n // Detect resource signature: (url, variables, extra)\n const isResource = allArgs.length >= 2 && allArgs[0] instanceof URL\n const extra = isResource ? allArgs[2] : allArgs[1]\n const argsOrVars = isResource ? allArgs[1] : allArgs[0]\n\n try {\n // x402 v2 MCP transport: prefer the in-band payment payload from\n // params._meta[\"x402/payment\"]. Re-encode it into the access token\n // string the verify/settle path expects and present it via the same\n // extra/headers shape the auth flow reads, so the in-band payload takes\n // precedence over the Authorization header (kept as a deprecated\n // fallback when the in-band payload is absent). The RAW extra is still\n // forwarded to the user handler below.\n const paymentPayload = readPaymentPayload(extra)\n let authExtra = extra\n if (paymentPayload) {\n const token = encodeAccessToken(paymentPayload)\n // Synthesize an auth-only extra carrying the in-band token. This\n // intentionally drops the rest of `extra` for the AUTH call only; the\n // RAW `extra` (with `_meta`) is still forwarded to the user handler below.\n authExtra = { requestInfo: { headers: { authorization: `Bearer ${token}` } } }\n } else if (!authHeaderDeprecationWarned) {\n authHeaderDeprecationWarned = true\n console.warn(\n '[x402] No _meta[\"x402/payment\"] on the MCP request; falling back to the ' +\n 'Authorization header (deprecated under the x402 v2 MCP transport).',\n )\n }\n\n // 1. Authenticate request\n const authResult = await this.authenticator.authenticate(\n authExtra,\n { planId: configuredPlanId, maxAmount: options?.maxAmount },\n this.config.agentId,\n this.config.serverName,\n name,\n kind,\n argsOrVars,\n )\n\n // 2. Pre-calculate credits if they are fixed (not a function)\n // This allows handlers to access credits during execution\n const creditsOption = options?.credits\n const isFixedCredits = typeof creditsOption === 'bigint' || creditsOption === undefined\n const preCalculatedCredits = isFixedCredits\n ? this.creditsContext.resolve(creditsOption, argsOrVars, null, authResult)\n : undefined\n\n // Determine effective planId: explicit option overrides token-derived value\n const effectivePlanId = options?.planId ?? authResult.planId\n\n // 3. Build PaywallContext for handler (with extra wrapper for backward compatibility)\n const paywallContext = {\n authResult,\n credits: preCalculatedCredits,\n planId: authResult.planId,\n subscriberAddress: authResult.subscriberAddress,\n agentRequest: authResult.agentRequest,\n }\n\n // 4. Execute original handler with context\n const result = await (handler as any)(...allArgs, paywallContext)\n\n // 5. Resolve final credits to burn (may be different if credits are dynamic)\n const credits = isFixedCredits\n ? (preCalculatedCredits ?? 1n)\n : this.creditsContext.resolve(creditsOption, argsOrVars, result, authResult)\n\n // Update context with final resolved credits\n paywallContext.credits = credits\n\n // 6. If the result is an AsyncIterable (stream), redeem on completion\n if (isAsyncIterable(result)) {\n const onFinally = async () => {\n return await this.redeemCredits(\n effectivePlanId,\n authResult.token,\n authResult.subscriberAddress,\n credits,\n options,\n authResult.agentId,\n authResult.logicalUrl,\n authResult.httpUrl,\n 'POST',\n )\n }\n return wrapAsyncIterable(\n result,\n onFinally,\n effectivePlanId,\n authResult.subscriberAddress,\n credits,\n )\n }\n\n // 7. Non-streaming: redeem immediately\n const creditsResult = await this.redeemCredits(\n effectivePlanId,\n authResult.token,\n authResult.subscriberAddress,\n credits,\n options,\n authResult.agentId,\n authResult.logicalUrl,\n // fix: pre-existing arg order — fallbackEndpoint=httpUrl, httpVerb='POST'\n // (matches the streaming site above)\n authResult.httpUrl,\n 'POST',\n )\n\n // Settlement failed AFTER the tool executed: per the x402 v2 MCP\n // transport spec, do NOT return the tool's content — surface only the\n // payment error so a paid result is never delivered without payment\n // landing. (onRedeemError \"ignore\" therefore no longer delivers paid\n // content; \"propagate\" already threw a Misconfiguration in redeemCredits.)\n if (creditsResult && !creditsResult.success) {\n console.error(\n `[x402] settlement failed after tool execution; suppressing tool content. reason=${creditsResult.errorReason}`,\n )\n throw new SettlementFailedError(this.buildPaymentRequiredFromAuth(authResult))\n }\n\n // creditsResult is undefined for free / no-credit calls (no settlement\n // performed) — in that case the spec receipt is omitted. On success the\n // full receipt goes under the spec key; Nevermined observability is kept\n // under a namespaced key so it never collides with the spec shape.\n result._meta = {\n ...result._meta,\n ...(creditsResult && { [X402_PAYMENT_RESPONSE_META_KEY]: creditsResult }),\n [NEVERMINED_CREDITS_META_KEY]: {\n ...(creditsResult?.transaction && { txHash: creditsResult.transaction }),\n // `creditsRedeemed` is meaningless without this: a pay-as-you-go plan\n // holds no balance, so it reads '0' on a settle that DID charge the\n // buyer. Omitted (not '') when the settle carried no discriminator,\n // so a consumer can tell \"absent\" from \"present and empty\".\n ...(creditsResult?.billingModel && { billingModel: creditsResult.billingModel }),\n creditsRedeemed: creditsResult?.success\n ? (creditsResult.creditsRedeemed ?? credits.toString())\n : '0',\n remainingBalance: creditsResult?.remainingBalance,\n ...(creditsResult?.orderTx && { orderTx: creditsResult.orderTx }),\n planId: authResult.planId,\n subscriberAddress: authResult.subscriberAddress,\n success: creditsResult ? creditsResult.success : true,\n },\n }\n return result\n } catch (error) {\n // Payment-required (pre-execution, from auth) and settlement-failure\n // (post-execution) are surfaced in band as an error tool result for\n // tools. Resources/prompts have no tool-result error channel, so the\n // error propagates as a JSON-RPC error instead.\n if (error instanceof PaymentRequiredError && kind === 'tool') {\n return paymentRequiredResult(error.paymentRequired)\n }\n throw error\n }\n }\n }\n\n /**\n * Build a spec-shaped `PaymentRequired` dict for a settlement failure, from\n * the authenticated request context. Surfaced (with tool content suppressed)\n * when settlement fails after the tool has executed.\n */\n private buildPaymentRequiredFromAuth(authResult: AuthResult): Record<string, any> {\n const planId = authResult.planId || ''\n const paymentRequired = buildPaymentRequiredForPlans(planId ? [planId] : [''], {\n endpoint: authResult.logicalUrl || authResult.httpUrl,\n agentId: authResult.agentId,\n httpVerb: 'POST',\n environment: this.payments.getEnvironmentName(),\n }) as X402PaymentRequired & { error?: string }\n paymentRequired.error = 'settlement failed'\n return paymentRequired\n }\n\n /**\n * Redeem credits after successful request\n */\n private async redeemCredits(\n planId: string,\n token: string,\n subscriberAddress: Address,\n credits: bigint,\n options: PaywallOptions,\n agentId?: string,\n endpoint?: string,\n fallbackEndpoint?: string,\n httpVerb?: string,\n ): Promise<SettlePermissionsResult | undefined> {\n // No settlement for free / no-credit calls — signalled to the caller as\n // `undefined` so the spec receipt (_meta[\"x402/payment-response\"]) is omitted.\n if (!(credits && credits > 0n && subscriberAddress && planId)) {\n return undefined\n }\n\n const decoded = decodeAccessToken(token)\n const scheme = isValidScheme(decoded?.accepted?.scheme)\n ? decoded.accepted.scheme\n : 'nvm:erc4337'\n try {\n const paymentRequired: X402PaymentRequired = buildPaymentRequired(planId, {\n endpoint: endpoint || '',\n agentId,\n httpVerb,\n scheme,\n environment: this.payments.getEnvironmentName(),\n })\n\n return await this.payments.facilitator.settlePermissions({\n paymentRequired,\n x402AccessToken: token,\n maxAmount: credits,\n })\n } catch (primaryError) {\n // If logical URL fails and we have an HTTP URL fallback, retry with it\n let lastError: unknown = primaryError\n if (fallbackEndpoint) {\n try {\n const paymentRequired: X402PaymentRequired = buildPaymentRequired(planId, {\n endpoint: fallbackEndpoint,\n agentId,\n httpVerb,\n scheme,\n environment: this.payments.getEnvironmentName(),\n })\n\n return await this.payments.facilitator.settlePermissions({\n paymentRequired,\n x402AccessToken: token,\n maxAmount: credits,\n })\n } catch (fallbackError) {\n // Fallback also failed, use fallback error as the reported error\n lastError = fallbackError\n }\n }\n\n const errorReason = lastError instanceof Error ? lastError.message : String(lastError)\n console.error(`[x402] settle failed: ${errorReason}`)\n if (options.onRedeemError === 'propagate') {\n throw createRpcError(\n ERROR_CODES.Misconfiguration,\n `Failed to redeem credits: ${errorReason}`,\n )\n }\n // Default (\"ignore\"): return a failed result so the caller suppresses the\n // tool content and surfaces the in-band payment error (always-suppress\n // under the x402 v2 MCP transport).\n return { success: false, transaction: '', network: '', errorReason }\n }\n }\n}\n\n/**\n * Type guard to detect AsyncIterable values.\n */\nfunction isAsyncIterable<T = unknown>(value: any): value is AsyncIterable<T> {\n return value != null && typeof value[Symbol.asyncIterator] === 'function'\n}\n\n/**\n * Wrap an AsyncIterable with metadata injection at the end of the stream\n */\nfunction wrapAsyncIterable<T>(\n iterable: AsyncIterable<T>,\n onFinally: () => Promise<any>,\n planId: string,\n subscriberAddress: Address,\n credits: bigint,\n) {\n async function* generator() {\n let creditsResult: any = null\n try {\n for await (const chunk of iterable) {\n yield chunk as T\n }\n } finally {\n creditsResult = await onFinally()\n }\n\n // Yield a _meta chunk at the end with the redemption result.\n // NOTE: a stream cannot retroactively suppress already-yielded chunks, so a\n // post-execution settlement failure on a stream is only reported here in the\n // final _meta chunk (under nevermined/credits) — it cannot withhold content\n // the way a non-streaming tool result does. `creditsResult` is undefined for\n // free / no-credit calls.\n const settlement = creditsResult || undefined\n const metadataChunk = {\n _meta: {\n // Spec receipt only on a successful settlement.\n ...(settlement?.success && { [X402_PAYMENT_RESPONSE_META_KEY]: settlement }),\n // Nevermined-namespaced observability (NOT part of the x402 spec).\n [NEVERMINED_CREDITS_META_KEY]: {\n ...(settlement?.transaction && { txHash: settlement.transaction }),\n // See the non-streaming site: without `billingModel`, `creditsRedeemed`\n // cannot be read — it is '0' on a pay-as-you-go settle that charged.\n ...(settlement?.billingModel && { billingModel: settlement.billingModel }),\n creditsRedeemed: settlement?.success\n ? (settlement.creditsRedeemed ?? credits.toString())\n : '0',\n remainingBalance: settlement?.remainingBalance,\n ...(settlement?.orderTx && { orderTx: settlement.orderTx }),\n planId,\n subscriberAddress,\n success: settlement ? settlement.success : true,\n ...(settlement?.errorReason && { errorReason: settlement.errorReason }),\n },\n },\n }\n yield metadataChunk as T\n }\n return generator()\n}\n"]}
|
package/dist/mpp/fetch.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* environment. A caller branching on `instanceof MppError` to mean
|
|
18
18
|
* "the payment failed" gets exactly that, and no more.
|
|
19
19
|
*/
|
|
20
|
-
import type { DelegationConfig,
|
|
20
|
+
import type { DelegationConfig, MppTokenOptions } from '../common/types.js';
|
|
21
21
|
import type { MppReceipt } from './types.js';
|
|
22
22
|
/**
|
|
23
23
|
* Options for {@link mppFetch} / `MppAPI.fetch`.
|
|
@@ -139,8 +139,13 @@ export interface MppFetchResult {
|
|
|
139
139
|
*/
|
|
140
140
|
paid: boolean;
|
|
141
141
|
}
|
|
142
|
-
/**
|
|
143
|
-
|
|
142
|
+
/**
|
|
143
|
+
* Mints an MPP access token for a plan. Supplied by `MppAPI`.
|
|
144
|
+
*
|
|
145
|
+
* No `tokenVersion`: MPP carries none (nvm-monorepo#3266) — see
|
|
146
|
+
* {@link MppTokenOptions}.
|
|
147
|
+
*/
|
|
148
|
+
export type MppTokenMinter = (planId: string, agentId?: string, tokenOptions?: MppTokenOptions) => Promise<{
|
|
144
149
|
accessToken: string;
|
|
145
150
|
}>;
|
|
146
151
|
/**
|
package/dist/mpp/fetch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/mpp/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/mpp/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAI3E,OAAO,KAAK,EAAgB,UAAU,EAAE,MAAM,YAAY,CAAA;AAE1D;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,gBAAgB,EAAE,gBAAgB,CAAA;IAClC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;CACtC;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,QAAQ,EAAE,QAAQ,CAAA;IAClB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,UAAU,CAAA;IACpB;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB;;;;;;;OAOG;IACH,oBAAoB,EAAE,MAAM,CAAA;IAC5B;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;;;;;;;OAaG;IACH,IAAI,EAAE,OAAO,CAAA;CACd;AAED;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,CAC3B,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,eAAe,KAC3B,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAuPrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,cAAc,EACzB,KAAK,EAAE,MAAM,GAAG,GAAG,EACnB,IAAI,EAAE,WAAW,GAAG,SAAS,EAC7B,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,cAAc,CAAC,CA0KzB"}
|
package/dist/mpp/fetch.js
CHANGED
|
@@ -368,6 +368,12 @@ export async function mppFetch(mintToken, input, init, options) {
|
|
|
368
368
|
'so the 402 challenge from this endpoint cannot be replayed. Pass a replayable body ' +
|
|
369
369
|
'instead — a string, Buffer/ArrayBuffer/typed array, URLSearchParams, FormData or Blob.');
|
|
370
370
|
}
|
|
371
|
+
// No resource/verb binding and no `tokenVersion` here: MPP carries no
|
|
372
|
+
// token version at all (nvm-monorepo#3266). One MPP access token is
|
|
373
|
+
// presented across many challenges — genuinely distinct operations, not
|
|
374
|
+
// retries of one — so a per-token nonce would kill every buyer's second
|
|
375
|
+
// challenge, which is why the two protocols stopped sharing a version
|
|
376
|
+
// ladder. This loop mints a fresh token per credential regardless.
|
|
371
377
|
const { accessToken } = await mintToken(planId, options.agentId ?? challenge.request.agentId, { delegationConfig: options.delegationConfig });
|
|
372
378
|
const headers = new Headers(init?.headers ?? {});
|
|
373
379
|
const existingAuth = headers.get('authorization');
|
package/dist/mpp/fetch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/mpp/fetch.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The buyer half of MPP: pay a challenged endpoint with an existing Nevermined\n * delegation.\n *\n * The buyer learns nothing about MPP beyond calling this instead of `fetch`.\n * The plan comes out of the challenge, the credential is built from the\n * challenge plus an MPP-domain access token, and the request is retried once.\n *\n * Two error families are used deliberately:\n * - {@link PaymentsError} (`code: 'validation'`) for guards this call refuses\n * to even attempt — a bad argument, a challenge that violates a\n * caller-supplied constraint (`planId`, `maxCredits`), or a body that\n * cannot be replayed. None of these mean a payment failed; nothing was ever\n * attempted.\n * - {@link MppError} (and its typed subclasses) for what the wire actually\n * said: a rejected credential, a malformed challenge, an MPP-disabled\n * environment. A caller branching on `instanceof MppError` to mean\n * \"the payment failed\" gets exactly that, and no more.\n */\n\nimport { PaymentsError } from '../common/payments.error.js'\nimport type { DelegationConfig, X402TokenOptions } from '../common/types.js'\nimport { buildCredentialHeader, parseChallengeHeader, parseReceiptHeader } from './codec.js'\nimport { MppError, MppSpendOutcomeUnknownError, isRetryableMppCode, toMppError } from './errors.js'\nimport type { MppSpendReport } from './errors.js'\nimport type { MppChallenge, MppReceipt } from './types.js'\n\n/**\n * Options for {@link mppFetch} / `MppAPI.fetch`.\n *\n * @experimental The MPP buyer surface may change in a minor release.\n *\n * The request's `init.body`, if any, must be replayable **if the endpoint may\n * challenge the request**: a `ReadableStream` body throws a typed\n * {@link PaymentsError} once a 402 challenge actually requires a retry, since\n * the stream cannot be resent. A request that is never challenged sends a\n * stream body exactly once, exactly like plain `fetch`.\n *\n * This helper mints `nvm:erc4337` access tokens only in this release — a\n * buyer holding an `nvm:card-delegation` delegation cannot use it yet.\n */\nexport interface MppFetchOptions {\n /** The delegation that backs the payment — the same one x402 uses. */\n delegationConfig: DelegationConfig\n /**\n * Overrides the agent id the seller's challenge names — the minted token is\n * addressed to this agent id instead of whatever the challenge carries.\n * Unlike `planId`, this is not a guard: a mismatch is not checked or\n * refused, it simply replaces what the seller asked for. Leave unset to\n * honor the challenge as issued.\n */\n agentId?: string\n /** Fail before minting if the challenge names a different plan than this. */\n planId?: string\n /**\n * Budget for the WHOLE call, not for one challenge: the helper refuses to\n * mint whenever the credits named so far plus the credits this challenge\n * asks for would exceed it. A seller unilaterally names the price, and a\n * re-challenge names it again — a per-turn cap would therefore bound each\n * turn at `maxCredits` and the call at twice it, which is not what \"cap\"\n * reads as. `creditsPresented` on the result is the same running total, so\n * the two always speak about the same number.\n *\n * Must be a non-negative integer (a decimal string, a safe integer, or a\n * `bigint`); anything else is refused with a {@link PaymentsError} at entry,\n * before the first request, rather than mid-flight on the first 402.\n */\n maxCredits?: string | number | bigint\n}\n\n/**\n * What {@link mppFetch} / `MppAPI.fetch` resolves to.\n *\n * @experimental Fields may be added or change meaning in a minor release —\n * `settled` and `creditsPresented` both did during review.\n */\nexport interface MppFetchResult {\n /** The final response — the paid one when a payment happened. */\n response: Response\n /**\n * The decoded `Payment-Receipt`, when the server returned one and it\n * decoded cleanly. A malformed receipt never throws — it leaves this\n * absent (with a console warning) rather than destroying a response the\n * caller already paid for.\n */\n receipt?: MppReceipt\n /**\n * Whether the endpoint returned settlement evidence: a `Payment-Receipt` that\n * decoded cleanly and does not state failure outright. Never derived from the\n * HTTP status.\n *\n * `receipt.status` is read asymmetrically, on purpose. Success is NOT\n * recognized — `'success'` is the only value with agreement behind it, so\n * treating an unrecognized `'ok'`/`'completed'` as failure would report an\n * unpaid call that was in fact paid. An explicit negative (`'failed'`,\n * `'failure'`, `'declined'`, `'error'`) is a different thing from an unknown\n * vocabulary, and is excluded: `paid: true` on a receipt that says the\n * settlement failed is wrong in the one direction this field must never be\n * wrong. Anything else stays `settled: true` with `receipt` on the result, so\n * a caller paying third parties can judge the value itself.\n *\n * This SDK's own seller attaches a receipt only when settlement succeeded, so\n * the distinction is third-party exposure rather than in-ecosystem.\n */\n settled: boolean\n /**\n * How many credentials were minted and presented to the endpoint during\n * this call (0, 1 or 2). This is NOT the same as `settled`:\n * `credentialsPresented > 0` with `settled: false` means the seller may\n * already have burned credits for a credential whose fate is unknown to\n * the caller — treat that as \"do not blindly retry\", not as \"nothing\n * happened\".\n */\n credentialsPresented: number\n /**\n * TOTAL credits named by every challenge a credential was minted against\n * during this call, as a decimal string — summed, not the last turn's\n * amount, since a re-challenge is free to name a different price and the\n * caller is accounting for the call. Present whenever\n * `credentialsPresented > 0`.\n *\n * It is an **upper bound** on what may have burned, never a lower one. A\n * seller that answers a retryable code while replaying the identical\n * challenge id gets a second credential minted against that same challenge\n * (a code decides alone — see {@link mppFetch}), and against a seller that\n * keys single-use on the challenge id, as this SDK's middleware does, that\n * second credential is refused as a replay and burns nothing. The count is\n * deliberately not lowered for it: this field answers \"what could have left\",\n * and guessing which of a remote's credentials it honoured would answer a\n * question the buyer cannot see.\n */\n creditsPresented?: string\n /**\n * Whether a credential was presented AND the final response looks\n * successful: `response.ok && settled`. A 2xx with no receipt (a\n * settlement that silently failed) and a non-2xx with a receipt (settle-\n * then-error) are both `paid: false` — check `settled` and\n * `credentialsPresented` for the honest picture in either case.\n *\n * `ok: true, paid: false, credentialsPresented: 1` is a ROUTINE outcome, not\n * an exotic one: a seller whose handler streams or flushes has already sent\n * its headers when settlement runs, so `Payment-Receipt` cannot be attached\n * to a response that is already on the wire (this SDK's own middleware says\n * so where it settles detached). The credits were burned. Never read this\n * combination as \"the payment did not happen\" and retry.\n */\n paid: boolean\n}\n\n/** Mints an MPP access token for a plan. Supplied by `MppAPI`. */\nexport type MppTokenMinter = (\n planId: string,\n agentId?: string,\n tokenOptions?: X402TokenOptions,\n) => Promise<{ accessToken: string }>\n\n/**\n * Whether `body` is a `ReadableStream`.\n *\n * A stream is single-read: once a `fetch()` starts consuming it, the stream\n * is locked/disturbed, and a *second* `fetch()` with the same `init.body`\n * throws an opaque runtime `TypeError` rather than a typed error. Every other\n * `BodyInit` — `string`, `Buffer`/`ArrayBuffer`/typed arrays,\n * `URLSearchParams`, `FormData`, `Blob`, or no body at all — can be read more\n * than once, so a retry is safe.\n */\nfunction isNonReplayableBody(body: BodyInit | null | undefined): boolean {\n return typeof ReadableStream !== 'undefined' && body instanceof ReadableStream\n}\n\n/**\n * Cap on how much of a 402's error body is buffered before parsing it.\n *\n * The body is seller-controlled and read purely to look for a `code`, so a\n * hostile or broken endpoint answering a 402 with an unbounded stream must not\n * be able to exhaust the buyer's memory.\n *\n * A truncated read of a body large enough to matter will usually not parse; if\n * it does, the prefix necessarily contains the complete `code` object — a\n * truncated prefix of one JSON object can only parse once its closing brace is\n * inside the cap — so the classification is unchanged either way. The branch\n * keys on `JSON.parse` succeeding, never on \"truncated implies unparseable\",\n * which is false: `<complete JSON><whitespace><garbage>` parses after\n * truncation and throws before it.\n */\nconst MAX_ERROR_BODY_BYTES = 64 * 1024\n\n/**\n * Receipt `status` values that state failure outright, lower-cased.\n *\n * `MppReceipt.status` is a seller-set string and this SDK deliberately does not\n * try to recognize *success*: `'success'` is the only value with any agreement\n * behind it, so treating an unrecognized `'ok'`/`'completed'` as failure would\n * report an unpaid call that was in fact paid. The asymmetry is the point — an\n * explicit negative is not an unknown vocabulary, and reporting `paid: true`\n * for a receipt that says the settlement failed is wrong in the one direction\n * this field must never be wrong.\n *\n * Only unambiguous negatives belong here; anything genuinely ambiguous stays\n * unrecognized and is reported as settled, with `receipt` on the result for a\n * caller that wants to judge for itself.\n */\nconst EXPLICIT_FAILURE_RECEIPT_STATUSES: ReadonlySet<string> = new Set([\n 'failed',\n 'failure',\n 'declined',\n 'error',\n])\n\n/** Whether a decoded receipt states outright that the settlement did not happen. */\nfunction statesFailure(receipt: MppReceipt | undefined): boolean {\n return (\n receipt !== undefined &&\n typeof receipt.status === 'string' &&\n EXPLICIT_FAILURE_RECEIPT_STATUSES.has(receipt.status.trim().toLowerCase())\n )\n}\n\n/** The origin of `input`, or the raw value when it does not parse as a URL — used only to label a remote error. */\nfunction originOf(input: string | URL): string {\n try {\n return new URL(String(input)).origin\n } catch {\n return String(input)\n }\n}\n\n/**\n * Parses a challenge header, swallowing any decode failure into `null`.\n *\n * Used only to peek at a re-challenge's freshness inside the retry gate — a\n * garbled re-challenge is simply \"not a fresh challenge\", which the gate\n * already treats as terminal. The FIRST challenge of a 402 is never parsed\n * this leniently: see the `try`/`catch` around {@link parseChallengeHeader}\n * in {@link mppFetch}, which throws a typed error instead, since minting\n * against a challenge this function could not even parse would be worse.\n */\nfunction tryParseChallenge(headerValue: string): MppChallenge | null {\n try {\n return parseChallengeHeader(headerValue)\n } catch {\n return null\n }\n}\n\n/**\n * Refuses a challenge whose `credits` is not a decimal string before it is\n * ever minted against.\n *\n * `parseChallengeHeader` guarantees `credits` is a string OR a number and\n * coerces the number (`isValidChallengeRequestShape` / `toChallengeRequest` in\n * `codec.ts`), so what survives to here and still needs refusing is a string\n * that is not a non-negative integer — `'2.5'`, `'-1'`, `'1e3'`, `'abc'`. Each\n * of those would otherwise reach `BigInt()` in the cap comparison as a raw\n * `SyntaxError`, or the mint as a price nothing can account for.\n *\n * `planId` is NOT re-checked here: the codec already rejects a non-string or\n * empty `planId` outright, so a second guard for it was unreachable code that\n * read as coverage.\n */\nfunction assertValidChallengeRequest(challenge: MppChallenge, input: string | URL): void {\n const credits: unknown = challenge.request?.credits\n if (typeof credits !== 'string' || !/^\\d+$/.test(credits)) {\n throw new MppError(\n `The MPP challenge from ${originOf(input)} names a non-decimal-string credits value ` +\n `(${JSON.stringify(credits)}); refusing to mint.`,\n )\n }\n}\n\n/**\n * Validates `options.maxCredits` at entry and normalizes it to a `bigint`.\n *\n * At entry, not at the comparison: `maxCredits` is a caller argument, and a\n * caller mistake must surface before the first request rather than mid-flight\n * on whatever 402 happens to arrive — and as a {@link PaymentsError}, which is\n * what this module documents for bad arguments, rather than the raw\n * `SyntaxError`/`RangeError` that `BigInt('abc')` or `BigInt(1.5)` throws.\n */\nfunction parseMaxCredits(value: string | number | bigint | undefined): bigint | undefined {\n if (value === undefined) return undefined\n const refuse = () =>\n PaymentsError.validation(\n `maxCredits must be a non-negative integer (decimal string, safe integer or bigint), ` +\n `got ${JSON.stringify(typeof value === 'bigint' ? String(value) : value)}`,\n )\n if (typeof value === 'bigint') {\n if (value < 0n) throw refuse()\n return value\n }\n if (typeof value === 'number') {\n if (!Number.isSafeInteger(value) || value < 0) throw refuse()\n return BigInt(value)\n }\n if (typeof value === 'string' && /^\\d+$/.test(value.trim())) return BigInt(value.trim())\n throw refuse()\n}\n\n/**\n * Reads at most {@link MAX_ERROR_BODY_BYTES} of a response body as text.\n *\n * Streams the clone and stops at the cap instead of `text()`-ing it, so an\n * endpoint answering a 402 with an endless body cannot be used to exhaust the\n * buyer. A runtime whose `Response` exposes no readable `body` (or a test\n * double) falls back to `text()` with the same cap applied afterwards — the\n * bound is then advisory, which is why the streaming path is preferred.\n */\nasync function readBoundedText(response: Response): Promise<string> {\n const body = response.body\n if (!body || typeof body.getReader !== 'function') {\n return (await response.text()).slice(0, MAX_ERROR_BODY_BYTES)\n }\n const reader = body.getReader()\n const chunks: Uint8Array[] = []\n let size = 0\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n if (!value) continue\n // Truncate the chunk that crosses the cap rather than retaining it whole:\n // the transport chooses the chunk size, so \"stop after the chunk that\n // crossed\" would let one huge chunk defeat the bound entirely.\n const room = MAX_ERROR_BODY_BYTES - size\n if (value.byteLength >= room) {\n chunks.push(value.subarray(0, room))\n size = MAX_ERROR_BODY_BYTES\n break\n }\n chunks.push(value)\n size += value.byteLength\n }\n } finally {\n // NOT awaited, deliberately: this reader is one branch of the tee that\n // `clone()` creates, and awaiting its cancel deadlocks while the sibling\n // branch (the response the caller still holds) is unread — verified, it\n // hangs rather than resolving. Signalling the cancel is enough.\n reader.cancel().catch(() => undefined)\n }\n const joined = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) {\n joined.set(chunk, offset)\n offset += chunk.byteLength\n }\n return new TextDecoder().decode(joined)\n}\n\n/**\n * Reads the error body of a challenged retry.\n *\n * A retry that comes back 402 is either \"your credential was refused\"\n * (terminal) or \"here is a fresh challenge\" (retryable once). An unreadable\n * or non-JSON body — an HTML WAF/CDN page, a truncated response, a\n * disturbed-body clone failure — is neither: it is treated as terminal by\n * the caller, since it is not evidence of anything retryable.\n *\n * A body that is EMPTY or whitespace-only is not in that class. It is an\n * ordinary HTTP shape — the one the opening 402 is free to use — and carries no\n * code, so it is reported as \"no code\" rather than as unreadable. Reported as\n * unreadable it would suppress the documented fresh-challenge retry against a\n * seller that did nothing wrong, and blame a WAF that is not there. Truncated\n * bodies are non-empty by construction, so this cannot loosen that path.\n */\nasync function readMppErrorCode(\n response: Response,\n): Promise<{ code?: string; message: string; bodyUnreadable?: boolean }> {\n let raw: string\n try {\n raw = await readBoundedText(response.clone())\n } catch (err) {\n return {\n message: `MPP 402 body could not be read: ${err instanceof Error ? err.message : String(err)}`,\n bodyUnreadable: true,\n }\n }\n if (raw.trim() === '') return { message: 'MPP 402 carried no body' }\n try {\n const body = JSON.parse(raw)\n // A non-compliant seller can send a body shaped `{ error: { reason: '...' } }`\n // — no `message`, and `error` itself an object rather than a string.\n // Coerced to a string here, once, so every caller of this function (in\n // particular the terminal-throw's `message.slice(0, 200)`) can treat\n // `message` as always a string instead of risking a raw TypeError.\n const rawMessage = body?.message ?? body?.error\n const message = typeof rawMessage === 'string' ? rawMessage : 'MPP request failed'\n // `code` is only a code when it is a non-empty string. `{\"code\": null}` is\n // a routine way to serialize \"no code\", and taking it literally would both\n // store a `null` in a field typed `code?: string` and skip the\n // fresh-challenge fallback — which is gated on `code === undefined` — so a\n // legitimate re-challenge from such a seller would read as terminal.\n const code = typeof body?.code === 'string' && body.code !== '' ? body.code : undefined\n return { code, message }\n } catch {\n return {\n message: `MPP 402 was not JSON (likely a proxy or WAF page): ${raw.slice(0, 200)}`,\n bodyUnreadable: true,\n }\n }\n}\n\n/**\n * Performs the request, paying an MPP challenge if one comes back.\n *\n * At most one re-challenge cycle is followed: a seller that keeps challenging\n * a freshly paid credential is not going to be satisfied by looping, and a\n * loop would burn a credential per turn.\n *\n * The default on a retry-turn 402 is to STOP, not to pay again. **A code, when\n * present, decides alone**: one {@link isRetryableMppCode} accepts\n * (`BCK.MPP.0004` expired, `BCK.MPP.0005` body-digest mismatch) is retried,\n * every other code is terminal — including a non-`BCK.MPP.*` one, e.g. the\n * `network_error`/`http_500`-shaped code `MppAPI.post` can synthesize and this\n * repo's own seller forwards. The challenge id is not consulted on that path,\n * so a retryable code replaying the identical id does re-mint; `maxCredits`,\n * not id-freshness, is what bounds what that can cost.\n *\n * **With no code**, freshness is the whole signal: a challenge whose `id`\n * differs from the one just presented is a real re-challenge and is retried\n * once, while the identical id replayed, an unparseable challenge or an\n * unreadable body are terminal — a credential already proven invalid is never\n * paid for twice.\n *\n * ### What throws and what comes back as a 402\n *\n * Only a rejection the remote NAMED throws. Three dead ends RETURN the 402\n * instead, because the response is evidence the caller may need and throwing\n * would discard it:\n *\n * 1. A 402 with no USABLE `Payment` challenge (either turn): no\n * `WWW-Authenticate`, another scheme, or a `Payment` challenge missing a\n * required param — {@link parseChallengeHeader} yields nothing for all\n * three, so a seller that announced `Payment` and then sent it malformed\n * lands here too, not only one that does not speak MPP.\n * `credentialsPresented: 0`.\n * 2. A retry-turn 402 that IS retryable but carries no challenge to retry\n * against, so the next turn finds nothing to mint for.\n * 3. The one re-challenge cycle spent: two credentials presented and the\n * seller still answering 402.\n *\n * In cases 2 and 3 a credential WAS presented and may have been burned, so the\n * result is `paid: false` with a non-zero `credentialsPresented` and the last\n * 402 as `response`. Checking `response.ok` is therefore not optional — a\n * resolved promise does not mean the request was paid for.\n *\n * Every throw raised after a credential was presented carries the same\n * accounting as {@link MppSpendReport}, readable with `mppSpendOf(error)`,\n * including a transport failure on the credential-bearing retry (wrapped as\n * {@link MppSpendOutcomeUnknownError} so `instanceof MppError` catches it).\n */\nexport async function mppFetch(\n mintToken: MppTokenMinter,\n input: string | URL,\n init: RequestInit | undefined,\n options: MppFetchOptions,\n): Promise<MppFetchResult> {\n const maxCredits = parseMaxCredits(options.maxCredits)\n const maxChallenges = 2\n let response = await fetch(input, init)\n let credentialsPresented = 0\n let creditsPresentedTotal = 0n\n let lastChallengeId: string | undefined\n\n /** The accounting as it stands right now — reported identically on the return and the throw paths. */\n const spend = (): MppSpendReport => ({\n credentialsPresented,\n ...(credentialsPresented > 0 && { creditsPresented: creditsPresentedTotal.toString() }),\n ...(lastChallengeId !== undefined && { challengeId: lastChallengeId }),\n })\n\n for (let attempt = 0; attempt < maxChallenges; attempt++) {\n if (response.status !== 402) break\n\n const challengeHeader = response.headers.get('www-authenticate')\n if (!challengeHeader) break\n\n // Everything from here to the end of the turn can throw AFTER a credential\n // has been presented on an earlier turn — and, past the mint, after one has\n // been presented on this one. A single boundary attaches the accounting to\n // whatever comes out, so no exit from this function is silent about money.\n try {\n let challenge: MppChallenge | null\n try {\n challenge = parseChallengeHeader(challengeHeader)\n } catch (err) {\n throw new MppError(\n `The 402 from ${originOf(input)} carried a malformed MPP challenge ` +\n `(${err instanceof Error ? err.message : String(err)}). No payment was attempted.`,\n )\n }\n if (!challenge) break\n assertValidChallengeRequest(challenge, input)\n\n const planId = challenge.request.planId\n if (options.planId && options.planId !== planId) {\n throw PaymentsError.validation(\n `MPP challenge names plan ${planId}, but plan ${options.planId} was pinned by the caller`,\n )\n }\n\n // The cap bounds the CALL: what this challenge asks for is added to what\n // earlier turns already committed. Bounding each turn separately would\n // let a seller collect `maxCredits` twice by re-challenging once.\n const credits = BigInt(challenge.request.credits)\n if (maxCredits !== undefined && creditsPresentedTotal + credits > maxCredits) {\n throw PaymentsError.validation(\n `MPP challenge asks for ${challenge.request.credits} credits, which would take this call to ` +\n `${creditsPresentedTotal + credits}, above the caller's cap of ${maxCredits}` +\n (credentialsPresented > 0\n ? ` (${creditsPresentedTotal} already presented on ${credentialsPresented} credential(s))`\n : ''),\n )\n }\n\n // A retry resends init.body verbatim. A ReadableStream is single-read —\n // the first fetch() above already consumed it once — so replaying it now\n // would throw an opaque runtime TypeError. Checked here, at the point a\n // retry is actually about to happen, not before the (harmless) first\n // attempt: whether this endpoint ever challenges is not known ahead of\n // time, and a stream body against a non-challenging endpoint is fine.\n if (isNonReplayableBody(init?.body)) {\n throw PaymentsError.validation(\n 'payments.mpp.fetch cannot retry a ReadableStream request body: streams are single-read, ' +\n 'so the 402 challenge from this endpoint cannot be replayed. Pass a replayable body ' +\n 'instead — a string, Buffer/ArrayBuffer/typed array, URLSearchParams, FormData or Blob.',\n )\n }\n\n const { accessToken } = await mintToken(\n planId,\n options.agentId ?? challenge.request.agentId,\n { delegationConfig: options.delegationConfig },\n )\n\n const headers = new Headers(init?.headers ?? {})\n const existingAuth = headers.get('authorization')\n const credential = buildCredentialHeader(challenge, { accessToken })\n // Append, not replace: a caller authenticating to the resource server\n // with its own Authorization (the normal shape for a metered API) must\n // not have that credential stripped on the request that costs money.\n // Our own seller's extractPaymentScheme was hardened for exactly this\n // multi-scheme shape.\n headers.set('authorization', existingAuth ? `${credential}, ${existingAuth}` : credential)\n\n // Counted BEFORE the request, not after: the credential is on the wire as\n // soon as fetch() is called, so a rejection here (disconnect, DNS, the\n // caller's AbortSignal) must not report zero credentials presented. The\n // seller may already have verified and burned it.\n credentialsPresented += 1\n creditsPresentedTotal += credits\n lastChallengeId = challenge.id\n response = await fetch(input, { ...init, headers })\n\n if (response.status === 402) {\n const { code, message, bodyUnreadable } = await readMppErrorCode(response)\n\n let isFreshChallenge = false\n if (code === undefined && !bodyUnreadable) {\n const nextChallengeHeader = response.headers.get('www-authenticate')\n const nextChallenge = nextChallengeHeader ? tryParseChallenge(nextChallengeHeader) : null\n isFreshChallenge = !!nextChallenge && nextChallenge.id !== challenge.id\n }\n\n if (!isRetryableMppCode(code) && !isFreshChallenge) {\n throw toMppError(\n code,\n `${originOf(input)} rejected the credential: ${message.slice(0, 200)}`,\n )\n }\n // Otherwise the seller genuinely re-challenged (expired, a digest\n // mismatch, or — for a seller that sends no code, including THIS repo's\n // middleware when verification fails for infrastructure reasons — a\n // fresh id); the loop takes one more turn and mints a NEW credential\n // against the fresh challenge — the old one is not re-presented.\n continue\n }\n } catch (err) {\n throw asSpendAwareError(err, spend(), input)\n }\n\n const receiptHeader = response.headers.get('payment-receipt')\n let receipt: MppReceipt | undefined\n if (receiptHeader) {\n try {\n receipt = parseReceiptHeader(receiptHeader)\n } catch (err) {\n // The receipt is decorative (\"unsigned by design, and carries no\n // balance\" — see MppReceipt) and receipt? is optional precisely so\n // it can be absent. A failed decode must not destroy the response\n // the caller already paid for.\n console.warn(\n '[payments.mpp.fetch] payment may have succeeded but the Payment-Receipt could not be ' +\n `decoded: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n }\n\n const settled = receipt !== undefined && !statesFailure(receipt)\n return {\n response,\n settled,\n paid: response.ok && settled,\n credentialsPresented,\n ...(credentialsPresented > 0 && { creditsPresented: creditsPresentedTotal.toString() }),\n ...(receipt && { receipt }),\n }\n }\n\n // Every dead end documented on this function lands here: no challenge to pay,\n // a retryable 402 with no challenge to retry against, or the re-challenge\n // budget spent. `spend()` is what tells those apart — the last two carry a\n // non-zero `credentialsPresented`.\n return {\n response,\n settled: false,\n paid: false,\n credentialsPresented,\n ...(credentialsPresented > 0 && { creditsPresented: creditsPresentedTotal.toString() }),\n }\n}\n\n/**\n * Attaches spend accounting to whatever is escaping, and wraps a raw\n * transport failure that happened with a credential already on the wire.\n *\n * **Nothing is attached when nothing was presented.** `{ credentialsPresented: 0 }`\n * is a truthy object, so annotating a first-turn argument failure would send a\n * caller following the documented `if (mppSpendOf(err))` pattern into the\n * \"credits may already be burned, do not retry\" branch on a plain validation\n * error — making the field useless for the one decision it exists to inform.\n *\n * With that, three cases:\n * - An {@link MppError} (or the {@link PaymentsError} a caller-constraint guard\n * throws on the re-challenge turn) is annotated and rethrown AS-IS: the type\n * a caller branches on must not change just because money is now reported.\n * - Anything else with a credential already presented becomes an\n * {@link MppSpendOutcomeUnknownError}, so it reaches the `instanceof MppError`\n * handler this module documents instead of escaping as a raw `TypeError`.\n * - Anything else with nothing presented is rethrown untouched — nothing was\n * spent, and dressing up a plain network fault would only obscure it.\n */\nfunction asSpendAwareError(err: unknown, spend: MppSpendReport, input: string | URL): unknown {\n if (spend.credentialsPresented === 0) return err\n if (err instanceof MppError || err instanceof PaymentsError) {\n try {\n const annotated = err as { spend?: MppSpendReport }\n annotated.spend = spend\n return err\n } catch {\n // A frozen or otherwise non-extensible error cannot carry the report, and\n // silently returning it would recreate exactly the invisible-spend failure\n // this boundary exists to close. Fall through to the wrapper below, which\n // holds the report in its own field.\n }\n }\n const detail = err instanceof Error ? err.message : String(err)\n return new MppSpendOutcomeUnknownError(\n err instanceof MppError || err instanceof PaymentsError\n ? `The MPP credential was sent to ${originOf(input)} and the call then failed with an error that ` +\n `could not carry its own spend report (${detail}), so ${spend.creditsPresented} credits may or ` +\n 'may not have been burned. Do not blindly retry.'\n : `The MPP credential was sent to ${originOf(input)} but the request failed before any response ` +\n `was read (${detail}), so ${spend.creditsPresented} credits may or may not have been burned. ` +\n 'Do not blindly retry.',\n err,\n spend,\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/mpp/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAE3D,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC5F,OAAO,EAAE,QAAQ,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AA0InG;;;;;;;;;GASG;AACH,SAAS,mBAAmB,CAAC,IAAiC;IAC5D,OAAO,OAAO,cAAc,KAAK,WAAW,IAAI,IAAI,YAAY,cAAc,CAAA;AAChF,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,oBAAoB,GAAG,EAAE,GAAG,IAAI,CAAA;AAEtC;;;;;;;;;;;;;;GAcG;AACH,MAAM,iCAAiC,GAAwB,IAAI,GAAG,CAAC;IACrE,QAAQ;IACR,SAAS;IACT,UAAU;IACV,OAAO;CACR,CAAC,CAAA;AAEF,oFAAoF;AACpF,SAAS,aAAa,CAAC,OAA+B;IACpD,OAAO,CACL,OAAO,KAAK,SAAS;QACrB,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ;QAClC,iCAAiC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAC3E,CAAA;AACH,CAAC;AAED,mHAAmH;AACnH,SAAS,QAAQ,CAAC,KAAmB;IACnC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAA;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;IACtB,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,iBAAiB,CAAC,WAAmB;IAC5C,IAAI,CAAC;QACH,OAAO,oBAAoB,CAAC,WAAW,CAAC,CAAA;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAS,2BAA2B,CAAC,SAAuB,EAAE,KAAmB;IAC/E,MAAM,OAAO,GAAY,SAAS,CAAC,OAAO,EAAE,OAAO,CAAA;IACnD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,QAAQ,CAChB,0BAA0B,QAAQ,CAAC,KAAK,CAAC,4CAA4C;YACnF,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,sBAAsB,CACpD,CAAA;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,eAAe,CAAC,KAA2C;IAClE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACzC,MAAM,MAAM,GAAG,GAAG,EAAE,CAClB,aAAa,CAAC,UAAU,CACtB,sFAAsF;QACpF,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAC7E,CAAA;IACH,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,KAAK,GAAG,EAAE;YAAE,MAAM,MAAM,EAAE,CAAA;QAC9B,OAAO,KAAK,CAAA;IACd,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;YAAE,MAAM,MAAM,EAAE,CAAA;QAC7D,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;IACtB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;IACxF,MAAM,MAAM,EAAE,CAAA;AAChB,CAAC;AAED;;;;;;;;GAQG;AACH,KAAK,UAAU,eAAe,CAAC,QAAkB;IAC/C,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAA;IAC1B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;QAClD,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAA;IAC/D,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAA;IAC/B,MAAM,MAAM,GAAiB,EAAE,CAAA;IAC/B,IAAI,IAAI,GAAG,CAAC,CAAA;IACZ,IAAI,CAAC;QACH,SAAS,CAAC;YACR,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;YAC3C,IAAI,IAAI;gBAAE,MAAK;YACf,IAAI,CAAC,KAAK;gBAAE,SAAQ;YACpB,0EAA0E;YAC1E,sEAAsE;YACtE,+DAA+D;YAC/D,MAAM,IAAI,GAAG,oBAAoB,GAAG,IAAI,CAAA;YACxC,IAAI,KAAK,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;gBAC7B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAA;gBACpC,IAAI,GAAG,oBAAoB,CAAA;gBAC3B,MAAK;YACP,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAClB,IAAI,IAAI,KAAK,CAAC,UAAU,CAAA;QAC1B,CAAC;IACH,CAAC;YAAS,CAAC;QACT,uEAAuE;QACvE,yEAAyE;QACzE,wEAAwE;QACxE,gEAAgE;QAChE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IACxC,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAA;IACnC,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QACzB,MAAM,IAAI,KAAK,CAAC,UAAU,CAAA;IAC5B,CAAC;IACD,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;AACzC,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,KAAK,UAAU,gBAAgB,CAC7B,QAAkB;IAElB,IAAI,GAAW,CAAA;IACf,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,mCAAmC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;YAC9F,cAAc,EAAE,IAAI;SACrB,CAAA;IACH,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAA;IACpE,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC5B,+EAA+E;QAC/E,qEAAqE;QACrE,uEAAuE;QACvE,qEAAqE;QACrE,mEAAmE;QACnE,MAAM,UAAU,GAAG,IAAI,EAAE,OAAO,IAAI,IAAI,EAAE,KAAK,CAAA;QAC/C,MAAM,OAAO,GAAG,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,oBAAoB,CAAA;QAClF,2EAA2E;QAC3E,2EAA2E;QAC3E,+DAA+D;QAC/D,2EAA2E;QAC3E,qEAAqE;QACrE,MAAM,IAAI,GAAG,OAAO,IAAI,EAAE,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;QACvF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,OAAO,EAAE,sDAAsD,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;YAClF,cAAc,EAAE,IAAI;SACrB,CAAA;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,SAAyB,EACzB,KAAmB,EACnB,IAA6B,EAC7B,OAAwB;IAExB,MAAM,UAAU,GAAG,eAAe,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;IACtD,MAAM,aAAa,GAAG,CAAC,CAAA;IACvB,IAAI,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IACvC,IAAI,oBAAoB,GAAG,CAAC,CAAA;IAC5B,IAAI,qBAAqB,GAAG,EAAE,CAAA;IAC9B,IAAI,eAAmC,CAAA;IAEvC,sGAAsG;IACtG,MAAM,KAAK,GAAG,GAAmB,EAAE,CAAC,CAAC;QACnC,oBAAoB;QACpB,GAAG,CAAC,oBAAoB,GAAG,CAAC,IAAI,EAAE,gBAAgB,EAAE,qBAAqB,CAAC,QAAQ,EAAE,EAAE,CAAC;QACvF,GAAG,CAAC,eAAe,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;KACvE,CAAC,CAAA;IAEF,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,aAAa,EAAE,OAAO,EAAE,EAAE,CAAC;QACzD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;YAAE,MAAK;QAElC,MAAM,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;QAChE,IAAI,CAAC,eAAe;YAAE,MAAK;QAE3B,2EAA2E;QAC3E,4EAA4E;QAC5E,2EAA2E;QAC3E,2EAA2E;QAC3E,IAAI,CAAC;YACH,IAAI,SAA8B,CAAA;YAClC,IAAI,CAAC;gBACH,SAAS,GAAG,oBAAoB,CAAC,eAAe,CAAC,CAAA;YACnD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,IAAI,QAAQ,CAChB,gBAAgB,QAAQ,CAAC,KAAK,CAAC,qCAAqC;oBAClE,IAAI,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,8BAA8B,CACrF,CAAA;YACH,CAAC;YACD,IAAI,CAAC,SAAS;gBAAE,MAAK;YACrB,2BAA2B,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;YAE7C,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAA;YACvC,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAChD,MAAM,aAAa,CAAC,UAAU,CAC5B,4BAA4B,MAAM,cAAc,OAAO,CAAC,MAAM,2BAA2B,CAC1F,CAAA;YACH,CAAC;YAED,yEAAyE;YACzE,uEAAuE;YACvE,kEAAkE;YAClE,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;YACjD,IAAI,UAAU,KAAK,SAAS,IAAI,qBAAqB,GAAG,OAAO,GAAG,UAAU,EAAE,CAAC;gBAC7E,MAAM,aAAa,CAAC,UAAU,CAC5B,0BAA0B,SAAS,CAAC,OAAO,CAAC,OAAO,0CAA0C;oBAC3F,GAAG,qBAAqB,GAAG,OAAO,+BAA+B,UAAU,EAAE;oBAC7E,CAAC,oBAAoB,GAAG,CAAC;wBACvB,CAAC,CAAC,KAAK,qBAAqB,yBAAyB,oBAAoB,iBAAiB;wBAC1F,CAAC,CAAC,EAAE,CAAC,CACV,CAAA;YACH,CAAC;YAED,wEAAwE;YACxE,yEAAyE;YACzE,wEAAwE;YACxE,qEAAqE;YACrE,uEAAuE;YACvE,sEAAsE;YACtE,IAAI,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;gBACpC,MAAM,aAAa,CAAC,UAAU,CAC5B,0FAA0F;oBACxF,qFAAqF;oBACrF,wFAAwF,CAC3F,CAAA;YACH,CAAC;YAED,sEAAsE;YACtE,oEAAoE;YACpE,wEAAwE;YACxE,wEAAwE;YACxE,sEAAsE;YACtE,mEAAmE;YACnE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,SAAS,CACrC,MAAM,EACN,OAAO,CAAC,OAAO,IAAI,SAAS,CAAC,OAAO,CAAC,OAAO,EAC5C,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAC/C,CAAA;YAED,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC,CAAA;YAChD,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;YACjD,MAAM,UAAU,GAAG,qBAAqB,CAAC,SAAS,EAAE,EAAE,WAAW,EAAE,CAAC,CAAA;YACpE,sEAAsE;YACtE,uEAAuE;YACvE,qEAAqE;YACrE,sEAAsE;YACtE,sBAAsB;YACtB,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,UAAU,KAAK,YAAY,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAA;YAE1F,0EAA0E;YAC1E,uEAAuE;YACvE,wEAAwE;YACxE,kDAAkD;YAClD,oBAAoB,IAAI,CAAC,CAAA;YACzB,qBAAqB,IAAI,OAAO,CAAA;YAChC,eAAe,GAAG,SAAS,CAAC,EAAE,CAAA;YAC9B,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;YAEnD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC5B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAA;gBAE1E,IAAI,gBAAgB,GAAG,KAAK,CAAA;gBAC5B,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,cAAc,EAAE,CAAC;oBAC1C,MAAM,mBAAmB,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;oBACpE,MAAM,aAAa,GAAG,mBAAmB,CAAC,CAAC,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;oBACzF,gBAAgB,GAAG,CAAC,CAAC,aAAa,IAAI,aAAa,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,CAAA;gBACzE,CAAC;gBAED,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;oBACnD,MAAM,UAAU,CACd,IAAI,EACJ,GAAG,QAAQ,CAAC,KAAK,CAAC,6BAA6B,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACvE,CAAA;gBACH,CAAC;gBACD,kEAAkE;gBAClE,wEAAwE;gBACxE,oEAAoE;gBACpE,qEAAqE;gBACrE,iEAAiE;gBACjE,SAAQ;YACV,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,iBAAiB,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,KAAK,CAAC,CAAA;QAC9C,CAAC;QAED,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;QAC7D,IAAI,OAA+B,CAAA;QACnC,IAAI,aAAa,EAAE,CAAC;YAClB,IAAI,CAAC;gBACH,OAAO,GAAG,kBAAkB,CAAC,aAAa,CAAC,CAAA;YAC7C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,iEAAiE;gBACjE,mEAAmE;gBACnE,kEAAkE;gBAClE,+BAA+B;gBAC/B,OAAO,CAAC,IAAI,CACV,uFAAuF;oBACrF,YAAY,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACjE,CAAA;YACH,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,KAAK,SAAS,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;QAChE,OAAO;YACL,QAAQ;YACR,OAAO;YACP,IAAI,EAAE,QAAQ,CAAC,EAAE,IAAI,OAAO;YAC5B,oBAAoB;YACpB,GAAG,CAAC,oBAAoB,GAAG,CAAC,IAAI,EAAE,gBAAgB,EAAE,qBAAqB,CAAC,QAAQ,EAAE,EAAE,CAAC;YACvF,GAAG,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC;SAC5B,CAAA;IACH,CAAC;IAED,8EAA8E;IAC9E,0EAA0E;IAC1E,2EAA2E;IAC3E,mCAAmC;IACnC,OAAO;QACL,QAAQ;QACR,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,KAAK;QACX,oBAAoB;QACpB,GAAG,CAAC,oBAAoB,GAAG,CAAC,IAAI,EAAE,gBAAgB,EAAE,qBAAqB,CAAC,QAAQ,EAAE,EAAE,CAAC;KACxF,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAS,iBAAiB,CAAC,GAAY,EAAE,KAAqB,EAAE,KAAmB;IACjF,IAAI,KAAK,CAAC,oBAAoB,KAAK,CAAC;QAAE,OAAO,GAAG,CAAA;IAChD,IAAI,GAAG,YAAY,QAAQ,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;QAC5D,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,GAAiC,CAAA;YACnD,SAAS,CAAC,KAAK,GAAG,KAAK,CAAA;YACvB,OAAO,GAAG,CAAA;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;YAC1E,2EAA2E;YAC3E,0EAA0E;YAC1E,qCAAqC;QACvC,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAC/D,OAAO,IAAI,2BAA2B,CACpC,GAAG,YAAY,QAAQ,IAAI,GAAG,YAAY,aAAa;QACrD,CAAC,CAAC,kCAAkC,QAAQ,CAAC,KAAK,CAAC,+CAA+C;YAC9F,yCAAyC,MAAM,SAAS,KAAK,CAAC,gBAAgB,kBAAkB;YAChG,iDAAiD;QACrD,CAAC,CAAC,kCAAkC,QAAQ,CAAC,KAAK,CAAC,8CAA8C;YAC7F,aAAa,MAAM,SAAS,KAAK,CAAC,gBAAgB,4CAA4C;YAC9F,uBAAuB,EAC7B,GAAG,EACH,KAAK,CACN,CAAA;AACH,CAAC","sourcesContent":["/**\n * The buyer half of MPP: pay a challenged endpoint with an existing Nevermined\n * delegation.\n *\n * The buyer learns nothing about MPP beyond calling this instead of `fetch`.\n * The plan comes out of the challenge, the credential is built from the\n * challenge plus an MPP-domain access token, and the request is retried once.\n *\n * Two error families are used deliberately:\n * - {@link PaymentsError} (`code: 'validation'`) for guards this call refuses\n * to even attempt — a bad argument, a challenge that violates a\n * caller-supplied constraint (`planId`, `maxCredits`), or a body that\n * cannot be replayed. None of these mean a payment failed; nothing was ever\n * attempted.\n * - {@link MppError} (and its typed subclasses) for what the wire actually\n * said: a rejected credential, a malformed challenge, an MPP-disabled\n * environment. A caller branching on `instanceof MppError` to mean\n * \"the payment failed\" gets exactly that, and no more.\n */\n\nimport { PaymentsError } from '../common/payments.error.js'\nimport type { DelegationConfig, MppTokenOptions } from '../common/types.js'\nimport { buildCredentialHeader, parseChallengeHeader, parseReceiptHeader } from './codec.js'\nimport { MppError, MppSpendOutcomeUnknownError, isRetryableMppCode, toMppError } from './errors.js'\nimport type { MppSpendReport } from './errors.js'\nimport type { MppChallenge, MppReceipt } from './types.js'\n\n/**\n * Options for {@link mppFetch} / `MppAPI.fetch`.\n *\n * @experimental The MPP buyer surface may change in a minor release.\n *\n * The request's `init.body`, if any, must be replayable **if the endpoint may\n * challenge the request**: a `ReadableStream` body throws a typed\n * {@link PaymentsError} once a 402 challenge actually requires a retry, since\n * the stream cannot be resent. A request that is never challenged sends a\n * stream body exactly once, exactly like plain `fetch`.\n *\n * This helper mints `nvm:erc4337` access tokens only in this release — a\n * buyer holding an `nvm:card-delegation` delegation cannot use it yet.\n */\nexport interface MppFetchOptions {\n /** The delegation that backs the payment — the same one x402 uses. */\n delegationConfig: DelegationConfig\n /**\n * Overrides the agent id the seller's challenge names — the minted token is\n * addressed to this agent id instead of whatever the challenge carries.\n * Unlike `planId`, this is not a guard: a mismatch is not checked or\n * refused, it simply replaces what the seller asked for. Leave unset to\n * honor the challenge as issued.\n */\n agentId?: string\n /** Fail before minting if the challenge names a different plan than this. */\n planId?: string\n /**\n * Budget for the WHOLE call, not for one challenge: the helper refuses to\n * mint whenever the credits named so far plus the credits this challenge\n * asks for would exceed it. A seller unilaterally names the price, and a\n * re-challenge names it again — a per-turn cap would therefore bound each\n * turn at `maxCredits` and the call at twice it, which is not what \"cap\"\n * reads as. `creditsPresented` on the result is the same running total, so\n * the two always speak about the same number.\n *\n * Must be a non-negative integer (a decimal string, a safe integer, or a\n * `bigint`); anything else is refused with a {@link PaymentsError} at entry,\n * before the first request, rather than mid-flight on the first 402.\n */\n maxCredits?: string | number | bigint\n}\n\n/**\n * What {@link mppFetch} / `MppAPI.fetch` resolves to.\n *\n * @experimental Fields may be added or change meaning in a minor release —\n * `settled` and `creditsPresented` both did during review.\n */\nexport interface MppFetchResult {\n /** The final response — the paid one when a payment happened. */\n response: Response\n /**\n * The decoded `Payment-Receipt`, when the server returned one and it\n * decoded cleanly. A malformed receipt never throws — it leaves this\n * absent (with a console warning) rather than destroying a response the\n * caller already paid for.\n */\n receipt?: MppReceipt\n /**\n * Whether the endpoint returned settlement evidence: a `Payment-Receipt` that\n * decoded cleanly and does not state failure outright. Never derived from the\n * HTTP status.\n *\n * `receipt.status` is read asymmetrically, on purpose. Success is NOT\n * recognized — `'success'` is the only value with agreement behind it, so\n * treating an unrecognized `'ok'`/`'completed'` as failure would report an\n * unpaid call that was in fact paid. An explicit negative (`'failed'`,\n * `'failure'`, `'declined'`, `'error'`) is a different thing from an unknown\n * vocabulary, and is excluded: `paid: true` on a receipt that says the\n * settlement failed is wrong in the one direction this field must never be\n * wrong. Anything else stays `settled: true` with `receipt` on the result, so\n * a caller paying third parties can judge the value itself.\n *\n * This SDK's own seller attaches a receipt only when settlement succeeded, so\n * the distinction is third-party exposure rather than in-ecosystem.\n */\n settled: boolean\n /**\n * How many credentials were minted and presented to the endpoint during\n * this call (0, 1 or 2). This is NOT the same as `settled`:\n * `credentialsPresented > 0` with `settled: false` means the seller may\n * already have burned credits for a credential whose fate is unknown to\n * the caller — treat that as \"do not blindly retry\", not as \"nothing\n * happened\".\n */\n credentialsPresented: number\n /**\n * TOTAL credits named by every challenge a credential was minted against\n * during this call, as a decimal string — summed, not the last turn's\n * amount, since a re-challenge is free to name a different price and the\n * caller is accounting for the call. Present whenever\n * `credentialsPresented > 0`.\n *\n * It is an **upper bound** on what may have burned, never a lower one. A\n * seller that answers a retryable code while replaying the identical\n * challenge id gets a second credential minted against that same challenge\n * (a code decides alone — see {@link mppFetch}), and against a seller that\n * keys single-use on the challenge id, as this SDK's middleware does, that\n * second credential is refused as a replay and burns nothing. The count is\n * deliberately not lowered for it: this field answers \"what could have left\",\n * and guessing which of a remote's credentials it honoured would answer a\n * question the buyer cannot see.\n */\n creditsPresented?: string\n /**\n * Whether a credential was presented AND the final response looks\n * successful: `response.ok && settled`. A 2xx with no receipt (a\n * settlement that silently failed) and a non-2xx with a receipt (settle-\n * then-error) are both `paid: false` — check `settled` and\n * `credentialsPresented` for the honest picture in either case.\n *\n * `ok: true, paid: false, credentialsPresented: 1` is a ROUTINE outcome, not\n * an exotic one: a seller whose handler streams or flushes has already sent\n * its headers when settlement runs, so `Payment-Receipt` cannot be attached\n * to a response that is already on the wire (this SDK's own middleware says\n * so where it settles detached). The credits were burned. Never read this\n * combination as \"the payment did not happen\" and retry.\n */\n paid: boolean\n}\n\n/**\n * Mints an MPP access token for a plan. Supplied by `MppAPI`.\n *\n * No `tokenVersion`: MPP carries none (nvm-monorepo#3266) — see\n * {@link MppTokenOptions}.\n */\nexport type MppTokenMinter = (\n planId: string,\n agentId?: string,\n tokenOptions?: MppTokenOptions,\n) => Promise<{ accessToken: string }>\n\n/**\n * Whether `body` is a `ReadableStream`.\n *\n * A stream is single-read: once a `fetch()` starts consuming it, the stream\n * is locked/disturbed, and a *second* `fetch()` with the same `init.body`\n * throws an opaque runtime `TypeError` rather than a typed error. Every other\n * `BodyInit` — `string`, `Buffer`/`ArrayBuffer`/typed arrays,\n * `URLSearchParams`, `FormData`, `Blob`, or no body at all — can be read more\n * than once, so a retry is safe.\n */\nfunction isNonReplayableBody(body: BodyInit | null | undefined): boolean {\n return typeof ReadableStream !== 'undefined' && body instanceof ReadableStream\n}\n\n/**\n * Cap on how much of a 402's error body is buffered before parsing it.\n *\n * The body is seller-controlled and read purely to look for a `code`, so a\n * hostile or broken endpoint answering a 402 with an unbounded stream must not\n * be able to exhaust the buyer's memory.\n *\n * A truncated read of a body large enough to matter will usually not parse; if\n * it does, the prefix necessarily contains the complete `code` object — a\n * truncated prefix of one JSON object can only parse once its closing brace is\n * inside the cap — so the classification is unchanged either way. The branch\n * keys on `JSON.parse` succeeding, never on \"truncated implies unparseable\",\n * which is false: `<complete JSON><whitespace><garbage>` parses after\n * truncation and throws before it.\n */\nconst MAX_ERROR_BODY_BYTES = 64 * 1024\n\n/**\n * Receipt `status` values that state failure outright, lower-cased.\n *\n * `MppReceipt.status` is a seller-set string and this SDK deliberately does not\n * try to recognize *success*: `'success'` is the only value with any agreement\n * behind it, so treating an unrecognized `'ok'`/`'completed'` as failure would\n * report an unpaid call that was in fact paid. The asymmetry is the point — an\n * explicit negative is not an unknown vocabulary, and reporting `paid: true`\n * for a receipt that says the settlement failed is wrong in the one direction\n * this field must never be wrong.\n *\n * Only unambiguous negatives belong here; anything genuinely ambiguous stays\n * unrecognized and is reported as settled, with `receipt` on the result for a\n * caller that wants to judge for itself.\n */\nconst EXPLICIT_FAILURE_RECEIPT_STATUSES: ReadonlySet<string> = new Set([\n 'failed',\n 'failure',\n 'declined',\n 'error',\n])\n\n/** Whether a decoded receipt states outright that the settlement did not happen. */\nfunction statesFailure(receipt: MppReceipt | undefined): boolean {\n return (\n receipt !== undefined &&\n typeof receipt.status === 'string' &&\n EXPLICIT_FAILURE_RECEIPT_STATUSES.has(receipt.status.trim().toLowerCase())\n )\n}\n\n/** The origin of `input`, or the raw value when it does not parse as a URL — used only to label a remote error. */\nfunction originOf(input: string | URL): string {\n try {\n return new URL(String(input)).origin\n } catch {\n return String(input)\n }\n}\n\n/**\n * Parses a challenge header, swallowing any decode failure into `null`.\n *\n * Used only to peek at a re-challenge's freshness inside the retry gate — a\n * garbled re-challenge is simply \"not a fresh challenge\", which the gate\n * already treats as terminal. The FIRST challenge of a 402 is never parsed\n * this leniently: see the `try`/`catch` around {@link parseChallengeHeader}\n * in {@link mppFetch}, which throws a typed error instead, since minting\n * against a challenge this function could not even parse would be worse.\n */\nfunction tryParseChallenge(headerValue: string): MppChallenge | null {\n try {\n return parseChallengeHeader(headerValue)\n } catch {\n return null\n }\n}\n\n/**\n * Refuses a challenge whose `credits` is not a decimal string before it is\n * ever minted against.\n *\n * `parseChallengeHeader` guarantees `credits` is a string OR a number and\n * coerces the number (`isValidChallengeRequestShape` / `toChallengeRequest` in\n * `codec.ts`), so what survives to here and still needs refusing is a string\n * that is not a non-negative integer — `'2.5'`, `'-1'`, `'1e3'`, `'abc'`. Each\n * of those would otherwise reach `BigInt()` in the cap comparison as a raw\n * `SyntaxError`, or the mint as a price nothing can account for.\n *\n * `planId` is NOT re-checked here: the codec already rejects a non-string or\n * empty `planId` outright, so a second guard for it was unreachable code that\n * read as coverage.\n */\nfunction assertValidChallengeRequest(challenge: MppChallenge, input: string | URL): void {\n const credits: unknown = challenge.request?.credits\n if (typeof credits !== 'string' || !/^\\d+$/.test(credits)) {\n throw new MppError(\n `The MPP challenge from ${originOf(input)} names a non-decimal-string credits value ` +\n `(${JSON.stringify(credits)}); refusing to mint.`,\n )\n }\n}\n\n/**\n * Validates `options.maxCredits` at entry and normalizes it to a `bigint`.\n *\n * At entry, not at the comparison: `maxCredits` is a caller argument, and a\n * caller mistake must surface before the first request rather than mid-flight\n * on whatever 402 happens to arrive — and as a {@link PaymentsError}, which is\n * what this module documents for bad arguments, rather than the raw\n * `SyntaxError`/`RangeError` that `BigInt('abc')` or `BigInt(1.5)` throws.\n */\nfunction parseMaxCredits(value: string | number | bigint | undefined): bigint | undefined {\n if (value === undefined) return undefined\n const refuse = () =>\n PaymentsError.validation(\n `maxCredits must be a non-negative integer (decimal string, safe integer or bigint), ` +\n `got ${JSON.stringify(typeof value === 'bigint' ? String(value) : value)}`,\n )\n if (typeof value === 'bigint') {\n if (value < 0n) throw refuse()\n return value\n }\n if (typeof value === 'number') {\n if (!Number.isSafeInteger(value) || value < 0) throw refuse()\n return BigInt(value)\n }\n if (typeof value === 'string' && /^\\d+$/.test(value.trim())) return BigInt(value.trim())\n throw refuse()\n}\n\n/**\n * Reads at most {@link MAX_ERROR_BODY_BYTES} of a response body as text.\n *\n * Streams the clone and stops at the cap instead of `text()`-ing it, so an\n * endpoint answering a 402 with an endless body cannot be used to exhaust the\n * buyer. A runtime whose `Response` exposes no readable `body` (or a test\n * double) falls back to `text()` with the same cap applied afterwards — the\n * bound is then advisory, which is why the streaming path is preferred.\n */\nasync function readBoundedText(response: Response): Promise<string> {\n const body = response.body\n if (!body || typeof body.getReader !== 'function') {\n return (await response.text()).slice(0, MAX_ERROR_BODY_BYTES)\n }\n const reader = body.getReader()\n const chunks: Uint8Array[] = []\n let size = 0\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n if (!value) continue\n // Truncate the chunk that crosses the cap rather than retaining it whole:\n // the transport chooses the chunk size, so \"stop after the chunk that\n // crossed\" would let one huge chunk defeat the bound entirely.\n const room = MAX_ERROR_BODY_BYTES - size\n if (value.byteLength >= room) {\n chunks.push(value.subarray(0, room))\n size = MAX_ERROR_BODY_BYTES\n break\n }\n chunks.push(value)\n size += value.byteLength\n }\n } finally {\n // NOT awaited, deliberately: this reader is one branch of the tee that\n // `clone()` creates, and awaiting its cancel deadlocks while the sibling\n // branch (the response the caller still holds) is unread — verified, it\n // hangs rather than resolving. Signalling the cancel is enough.\n reader.cancel().catch(() => undefined)\n }\n const joined = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) {\n joined.set(chunk, offset)\n offset += chunk.byteLength\n }\n return new TextDecoder().decode(joined)\n}\n\n/**\n * Reads the error body of a challenged retry.\n *\n * A retry that comes back 402 is either \"your credential was refused\"\n * (terminal) or \"here is a fresh challenge\" (retryable once). An unreadable\n * or non-JSON body — an HTML WAF/CDN page, a truncated response, a\n * disturbed-body clone failure — is neither: it is treated as terminal by\n * the caller, since it is not evidence of anything retryable.\n *\n * A body that is EMPTY or whitespace-only is not in that class. It is an\n * ordinary HTTP shape — the one the opening 402 is free to use — and carries no\n * code, so it is reported as \"no code\" rather than as unreadable. Reported as\n * unreadable it would suppress the documented fresh-challenge retry against a\n * seller that did nothing wrong, and blame a WAF that is not there. Truncated\n * bodies are non-empty by construction, so this cannot loosen that path.\n */\nasync function readMppErrorCode(\n response: Response,\n): Promise<{ code?: string; message: string; bodyUnreadable?: boolean }> {\n let raw: string\n try {\n raw = await readBoundedText(response.clone())\n } catch (err) {\n return {\n message: `MPP 402 body could not be read: ${err instanceof Error ? err.message : String(err)}`,\n bodyUnreadable: true,\n }\n }\n if (raw.trim() === '') return { message: 'MPP 402 carried no body' }\n try {\n const body = JSON.parse(raw)\n // A non-compliant seller can send a body shaped `{ error: { reason: '...' } }`\n // — no `message`, and `error` itself an object rather than a string.\n // Coerced to a string here, once, so every caller of this function (in\n // particular the terminal-throw's `message.slice(0, 200)`) can treat\n // `message` as always a string instead of risking a raw TypeError.\n const rawMessage = body?.message ?? body?.error\n const message = typeof rawMessage === 'string' ? rawMessage : 'MPP request failed'\n // `code` is only a code when it is a non-empty string. `{\"code\": null}` is\n // a routine way to serialize \"no code\", and taking it literally would both\n // store a `null` in a field typed `code?: string` and skip the\n // fresh-challenge fallback — which is gated on `code === undefined` — so a\n // legitimate re-challenge from such a seller would read as terminal.\n const code = typeof body?.code === 'string' && body.code !== '' ? body.code : undefined\n return { code, message }\n } catch {\n return {\n message: `MPP 402 was not JSON (likely a proxy or WAF page): ${raw.slice(0, 200)}`,\n bodyUnreadable: true,\n }\n }\n}\n\n/**\n * Performs the request, paying an MPP challenge if one comes back.\n *\n * At most one re-challenge cycle is followed: a seller that keeps challenging\n * a freshly paid credential is not going to be satisfied by looping, and a\n * loop would burn a credential per turn.\n *\n * The default on a retry-turn 402 is to STOP, not to pay again. **A code, when\n * present, decides alone**: one {@link isRetryableMppCode} accepts\n * (`BCK.MPP.0004` expired, `BCK.MPP.0005` body-digest mismatch) is retried,\n * every other code is terminal — including a non-`BCK.MPP.*` one, e.g. the\n * `network_error`/`http_500`-shaped code `MppAPI.post` can synthesize and this\n * repo's own seller forwards. The challenge id is not consulted on that path,\n * so a retryable code replaying the identical id does re-mint; `maxCredits`,\n * not id-freshness, is what bounds what that can cost.\n *\n * **With no code**, freshness is the whole signal: a challenge whose `id`\n * differs from the one just presented is a real re-challenge and is retried\n * once, while the identical id replayed, an unparseable challenge or an\n * unreadable body are terminal — a credential already proven invalid is never\n * paid for twice.\n *\n * ### What throws and what comes back as a 402\n *\n * Only a rejection the remote NAMED throws. Three dead ends RETURN the 402\n * instead, because the response is evidence the caller may need and throwing\n * would discard it:\n *\n * 1. A 402 with no USABLE `Payment` challenge (either turn): no\n * `WWW-Authenticate`, another scheme, or a `Payment` challenge missing a\n * required param — {@link parseChallengeHeader} yields nothing for all\n * three, so a seller that announced `Payment` and then sent it malformed\n * lands here too, not only one that does not speak MPP.\n * `credentialsPresented: 0`.\n * 2. A retry-turn 402 that IS retryable but carries no challenge to retry\n * against, so the next turn finds nothing to mint for.\n * 3. The one re-challenge cycle spent: two credentials presented and the\n * seller still answering 402.\n *\n * In cases 2 and 3 a credential WAS presented and may have been burned, so the\n * result is `paid: false` with a non-zero `credentialsPresented` and the last\n * 402 as `response`. Checking `response.ok` is therefore not optional — a\n * resolved promise does not mean the request was paid for.\n *\n * Every throw raised after a credential was presented carries the same\n * accounting as {@link MppSpendReport}, readable with `mppSpendOf(error)`,\n * including a transport failure on the credential-bearing retry (wrapped as\n * {@link MppSpendOutcomeUnknownError} so `instanceof MppError` catches it).\n */\nexport async function mppFetch(\n mintToken: MppTokenMinter,\n input: string | URL,\n init: RequestInit | undefined,\n options: MppFetchOptions,\n): Promise<MppFetchResult> {\n const maxCredits = parseMaxCredits(options.maxCredits)\n const maxChallenges = 2\n let response = await fetch(input, init)\n let credentialsPresented = 0\n let creditsPresentedTotal = 0n\n let lastChallengeId: string | undefined\n\n /** The accounting as it stands right now — reported identically on the return and the throw paths. */\n const spend = (): MppSpendReport => ({\n credentialsPresented,\n ...(credentialsPresented > 0 && { creditsPresented: creditsPresentedTotal.toString() }),\n ...(lastChallengeId !== undefined && { challengeId: lastChallengeId }),\n })\n\n for (let attempt = 0; attempt < maxChallenges; attempt++) {\n if (response.status !== 402) break\n\n const challengeHeader = response.headers.get('www-authenticate')\n if (!challengeHeader) break\n\n // Everything from here to the end of the turn can throw AFTER a credential\n // has been presented on an earlier turn — and, past the mint, after one has\n // been presented on this one. A single boundary attaches the accounting to\n // whatever comes out, so no exit from this function is silent about money.\n try {\n let challenge: MppChallenge | null\n try {\n challenge = parseChallengeHeader(challengeHeader)\n } catch (err) {\n throw new MppError(\n `The 402 from ${originOf(input)} carried a malformed MPP challenge ` +\n `(${err instanceof Error ? err.message : String(err)}). No payment was attempted.`,\n )\n }\n if (!challenge) break\n assertValidChallengeRequest(challenge, input)\n\n const planId = challenge.request.planId\n if (options.planId && options.planId !== planId) {\n throw PaymentsError.validation(\n `MPP challenge names plan ${planId}, but plan ${options.planId} was pinned by the caller`,\n )\n }\n\n // The cap bounds the CALL: what this challenge asks for is added to what\n // earlier turns already committed. Bounding each turn separately would\n // let a seller collect `maxCredits` twice by re-challenging once.\n const credits = BigInt(challenge.request.credits)\n if (maxCredits !== undefined && creditsPresentedTotal + credits > maxCredits) {\n throw PaymentsError.validation(\n `MPP challenge asks for ${challenge.request.credits} credits, which would take this call to ` +\n `${creditsPresentedTotal + credits}, above the caller's cap of ${maxCredits}` +\n (credentialsPresented > 0\n ? ` (${creditsPresentedTotal} already presented on ${credentialsPresented} credential(s))`\n : ''),\n )\n }\n\n // A retry resends init.body verbatim. A ReadableStream is single-read —\n // the first fetch() above already consumed it once — so replaying it now\n // would throw an opaque runtime TypeError. Checked here, at the point a\n // retry is actually about to happen, not before the (harmless) first\n // attempt: whether this endpoint ever challenges is not known ahead of\n // time, and a stream body against a non-challenging endpoint is fine.\n if (isNonReplayableBody(init?.body)) {\n throw PaymentsError.validation(\n 'payments.mpp.fetch cannot retry a ReadableStream request body: streams are single-read, ' +\n 'so the 402 challenge from this endpoint cannot be replayed. Pass a replayable body ' +\n 'instead — a string, Buffer/ArrayBuffer/typed array, URLSearchParams, FormData or Blob.',\n )\n }\n\n // No resource/verb binding and no `tokenVersion` here: MPP carries no\n // token version at all (nvm-monorepo#3266). One MPP access token is\n // presented across many challenges — genuinely distinct operations, not\n // retries of one — so a per-token nonce would kill every buyer's second\n // challenge, which is why the two protocols stopped sharing a version\n // ladder. This loop mints a fresh token per credential regardless.\n const { accessToken } = await mintToken(\n planId,\n options.agentId ?? challenge.request.agentId,\n { delegationConfig: options.delegationConfig },\n )\n\n const headers = new Headers(init?.headers ?? {})\n const existingAuth = headers.get('authorization')\n const credential = buildCredentialHeader(challenge, { accessToken })\n // Append, not replace: a caller authenticating to the resource server\n // with its own Authorization (the normal shape for a metered API) must\n // not have that credential stripped on the request that costs money.\n // Our own seller's extractPaymentScheme was hardened for exactly this\n // multi-scheme shape.\n headers.set('authorization', existingAuth ? `${credential}, ${existingAuth}` : credential)\n\n // Counted BEFORE the request, not after: the credential is on the wire as\n // soon as fetch() is called, so a rejection here (disconnect, DNS, the\n // caller's AbortSignal) must not report zero credentials presented. The\n // seller may already have verified and burned it.\n credentialsPresented += 1\n creditsPresentedTotal += credits\n lastChallengeId = challenge.id\n response = await fetch(input, { ...init, headers })\n\n if (response.status === 402) {\n const { code, message, bodyUnreadable } = await readMppErrorCode(response)\n\n let isFreshChallenge = false\n if (code === undefined && !bodyUnreadable) {\n const nextChallengeHeader = response.headers.get('www-authenticate')\n const nextChallenge = nextChallengeHeader ? tryParseChallenge(nextChallengeHeader) : null\n isFreshChallenge = !!nextChallenge && nextChallenge.id !== challenge.id\n }\n\n if (!isRetryableMppCode(code) && !isFreshChallenge) {\n throw toMppError(\n code,\n `${originOf(input)} rejected the credential: ${message.slice(0, 200)}`,\n )\n }\n // Otherwise the seller genuinely re-challenged (expired, a digest\n // mismatch, or — for a seller that sends no code, including THIS repo's\n // middleware when verification fails for infrastructure reasons — a\n // fresh id); the loop takes one more turn and mints a NEW credential\n // against the fresh challenge — the old one is not re-presented.\n continue\n }\n } catch (err) {\n throw asSpendAwareError(err, spend(), input)\n }\n\n const receiptHeader = response.headers.get('payment-receipt')\n let receipt: MppReceipt | undefined\n if (receiptHeader) {\n try {\n receipt = parseReceiptHeader(receiptHeader)\n } catch (err) {\n // The receipt is decorative (\"unsigned by design, and carries no\n // balance\" — see MppReceipt) and receipt? is optional precisely so\n // it can be absent. A failed decode must not destroy the response\n // the caller already paid for.\n console.warn(\n '[payments.mpp.fetch] payment may have succeeded but the Payment-Receipt could not be ' +\n `decoded: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n }\n\n const settled = receipt !== undefined && !statesFailure(receipt)\n return {\n response,\n settled,\n paid: response.ok && settled,\n credentialsPresented,\n ...(credentialsPresented > 0 && { creditsPresented: creditsPresentedTotal.toString() }),\n ...(receipt && { receipt }),\n }\n }\n\n // Every dead end documented on this function lands here: no challenge to pay,\n // a retryable 402 with no challenge to retry against, or the re-challenge\n // budget spent. `spend()` is what tells those apart — the last two carry a\n // non-zero `credentialsPresented`.\n return {\n response,\n settled: false,\n paid: false,\n credentialsPresented,\n ...(credentialsPresented > 0 && { creditsPresented: creditsPresentedTotal.toString() }),\n }\n}\n\n/**\n * Attaches spend accounting to whatever is escaping, and wraps a raw\n * transport failure that happened with a credential already on the wire.\n *\n * **Nothing is attached when nothing was presented.** `{ credentialsPresented: 0 }`\n * is a truthy object, so annotating a first-turn argument failure would send a\n * caller following the documented `if (mppSpendOf(err))` pattern into the\n * \"credits may already be burned, do not retry\" branch on a plain validation\n * error — making the field useless for the one decision it exists to inform.\n *\n * With that, three cases:\n * - An {@link MppError} (or the {@link PaymentsError} a caller-constraint guard\n * throws on the re-challenge turn) is annotated and rethrown AS-IS: the type\n * a caller branches on must not change just because money is now reported.\n * - Anything else with a credential already presented becomes an\n * {@link MppSpendOutcomeUnknownError}, so it reaches the `instanceof MppError`\n * handler this module documents instead of escaping as a raw `TypeError`.\n * - Anything else with nothing presented is rethrown untouched — nothing was\n * spent, and dressing up a plain network fault would only obscure it.\n */\nfunction asSpendAwareError(err: unknown, spend: MppSpendReport, input: string | URL): unknown {\n if (spend.credentialsPresented === 0) return err\n if (err instanceof MppError || err instanceof PaymentsError) {\n try {\n const annotated = err as { spend?: MppSpendReport }\n annotated.spend = spend\n return err\n } catch {\n // A frozen or otherwise non-extensible error cannot carry the report, and\n // silently returning it would recreate exactly the invisible-spend failure\n // this boundary exists to close. Fall through to the wrapper below, which\n // holds the report in its own field.\n }\n }\n const detail = err instanceof Error ? err.message : String(err)\n return new MppSpendOutcomeUnknownError(\n err instanceof MppError || err instanceof PaymentsError\n ? `The MPP credential was sent to ${originOf(input)} and the call then failed with an error that ` +\n `could not carry its own spend report (${detail}), so ${spend.creditsPresented} credits may or ` +\n 'may not have been burned. Do not blindly retry.'\n : `The MPP credential was sent to ${originOf(input)} but the request failed before any response ` +\n `was read (${detail}), so ${spend.creditsPresented} credits may or may not have been burned. ` +\n 'Do not blindly retry.',\n err,\n spend,\n )\n}\n"]}
|
package/dist/mpp/mpp-api.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* an MPP-domain access token and present it as a credential.
|
|
8
8
|
*/
|
|
9
9
|
import { BasePaymentsAPI } from '../api/base-payments.js';
|
|
10
|
-
import type {
|
|
10
|
+
import type { MppTokenOptions, PaymentOptions } from '../common/types.js';
|
|
11
11
|
import type { SettlePermissionsResult, VerifyPermissionsResult } from '../x402/facilitator-api.js';
|
|
12
12
|
import { type MppFetchOptions, type MppFetchResult } from './fetch.js';
|
|
13
13
|
/**
|
|
@@ -88,11 +88,17 @@ export declare class MppAPI extends BasePaymentsAPI {
|
|
|
88
88
|
/**
|
|
89
89
|
* Mints an access token signed under the `Nevermined-MPP` EIP-712 domain.
|
|
90
90
|
*
|
|
91
|
-
* Same inputs and same settlement rail as {@link X402TokenAPI.getX402AccessToken}
|
|
92
|
-
* the token verifies only on the MPP routes, which is
|
|
93
|
-
*
|
|
91
|
+
* Same inputs and same settlement rail as {@link X402TokenAPI.getX402AccessToken}
|
|
92
|
+
* minus `tokenVersion`; the token verifies only on the MPP routes, which is
|
|
93
|
+
* what keeps the two protocols isolated.
|
|
94
|
+
*
|
|
95
|
+
* MPP carries **no token version** (nvm-monorepo#3266): its single-use unit is
|
|
96
|
+
* the challenge, not the token, so one MPP token is presented across many
|
|
97
|
+
* challenges by design. The parameter type says so, and a JavaScript caller
|
|
98
|
+
* that sends one anyway is refused before the request — the backend answers
|
|
99
|
+
* `BCK.MPP.0007` for any value, `2` included.
|
|
94
100
|
*/
|
|
95
|
-
getMppAccessToken(planId: string, agentId?: string, tokenOptions?:
|
|
101
|
+
getMppAccessToken(planId: string, agentId?: string, tokenOptions?: MppTokenOptions): Promise<{
|
|
96
102
|
accessToken: string;
|
|
97
103
|
}>;
|
|
98
104
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mpp-api.d.ts","sourceRoot":"","sources":["../../src/mpp/mpp-api.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAQzD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"mpp-api.d.ts","sourceRoot":"","sources":["../../src/mpp/mpp-api.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAQzD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,KAAK,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAA;AAGlG,OAAO,EAAY,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAA;AAShF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAsB1E;AAmDD,MAAM,WAAW,uBAAuB;IACtC,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAA;IACd,sEAAsE;IACtE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;IACjC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,mFAAmF;IACnF,QAAQ,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAA;IAChB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAA;IAChB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAA;IAChB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GACvB,CAAC,uBAAuB,GAAG;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,GACrE,CAAC,uBAAuB,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAE3E,qBAAa,MAAO,SAAQ,eAAe;IACzC,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM;IAInD;;;;;;OAMG;IACG,cAAc,CAClB,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAa7C,yEAAyE;IACnE,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAIjF;;;;OAIG;IACG,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IAMzE;;;;;;;;;;;;OAYG;IACG,iBAAiB,CACrB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,eAAe,GAC7B,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAWnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACG,KAAK,CACT,KAAK,EAAE,MAAM,GAAG,GAAG,EACnB,IAAI,EAAE,WAAW,GAAG,SAAS,EAC7B,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,cAAc,CAAC;IAiB1B,OAAO,CAAC,UAAU;IAUlB;;;;;;;;;;;;;;;;;;;OAmBG;YACW,IAAI;CA4FnB"}
|
package/dist/mpp/mpp-api.js
CHANGED
|
@@ -137,9 +137,15 @@ export class MppAPI extends BasePaymentsAPI {
|
|
|
137
137
|
/**
|
|
138
138
|
* Mints an access token signed under the `Nevermined-MPP` EIP-712 domain.
|
|
139
139
|
*
|
|
140
|
-
* Same inputs and same settlement rail as {@link X402TokenAPI.getX402AccessToken}
|
|
141
|
-
* the token verifies only on the MPP routes, which is
|
|
142
|
-
*
|
|
140
|
+
* Same inputs and same settlement rail as {@link X402TokenAPI.getX402AccessToken}
|
|
141
|
+
* minus `tokenVersion`; the token verifies only on the MPP routes, which is
|
|
142
|
+
* what keeps the two protocols isolated.
|
|
143
|
+
*
|
|
144
|
+
* MPP carries **no token version** (nvm-monorepo#3266): its single-use unit is
|
|
145
|
+
* the challenge, not the token, so one MPP token is presented across many
|
|
146
|
+
* challenges by design. The parameter type says so, and a JavaScript caller
|
|
147
|
+
* that sends one anyway is refused before the request — the backend answers
|
|
148
|
+
* `BCK.MPP.0007` for any value, `2` included.
|
|
143
149
|
*/
|
|
144
150
|
async getMppAccessToken(planId, agentId, tokenOptions) {
|
|
145
151
|
const body = buildX402TokenRequestBody({
|
|
@@ -147,6 +153,7 @@ export class MppAPI extends BasePaymentsAPI {
|
|
|
147
153
|
agentId,
|
|
148
154
|
tokenOptions,
|
|
149
155
|
environmentName: this.environmentName,
|
|
156
|
+
protocol: 'mpp',
|
|
150
157
|
});
|
|
151
158
|
return this.post(API_URL_MPP_CREATE_PERMISSION, body);
|
|
152
159
|
}
|
package/dist/mpp/mpp-api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mpp-api.js","sourceRoot":"","sources":["../../src/mpp/mpp-api.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Machine Payments Protocol (MPP) API.\n *\n * MPP is a second payment framing over the unchanged Nevermined core: the same\n * plan, the same delegation and the same credit burn as x402, negotiated with\n * MPP headers. Sellers issue a challenge and redeem a credential; buyers mint\n * an MPP-domain access token and present it as a credential.\n */\n\nimport { BasePaymentsAPI } from '../api/base-payments.js'\nimport {\n API_URL_MPP_CHALLENGE,\n API_URL_MPP_CREATE_PERMISSION,\n API_URL_MPP_SETTLE,\n API_URL_MPP_VERIFY,\n} from '../api/nvm-api.js'\nimport { PaymentsError } from '../common/payments.error.js'\nimport type { PaymentOptions, X402TokenOptions } from '../common/types.js'\nimport type { SettlePermissionsResult, VerifyPermissionsResult } from '../x402/facilitator-api.js'\nimport { buildX402TokenRequestBody } from '../x402/token-request.js'\nimport { MppError, MppSettlementOutcomeUnknownError, toMppError } from './errors.js'\nimport { mppFetch, type MppFetchOptions, type MppFetchResult } from './fetch.js'\n\n/** Only whole, non-negative decimal digits — no sign, no leading/trailing\n * whitespace, no hex/octal/binary prefix, no fractional part. `BigInt(x)`\n * silently accepts all of those for a string input (`BigInt('0x10')` is 16,\n * `BigInt('')` is 0n, `BigInt(' 5 ')` is 5n), which a decimal-string\n * contract must reject rather than accept quietly. */\nconst DECIMAL_INTEGER_STRING = /^\\d+$/\n\n/**\n * Normalizes `credits` to the exact decimal-string amount that gets sealed\n * into the challenge and burned. A bare `.toString()` on a JS `number` can\n * emit scientific notation (`1e+21`), `\"NaN\"` or `\"Infinity\"` — all\n * forwarded unvalidated into an amount this PR's own docs describe as\n * having \"no post-hoc re-pricing as there is with x402\": a corrupted amount\n * here is not correctable downstream, it IS the amount.\n *\n * `BigInt(x)` renders an integer-valued number exactly (no scientific\n * notation) and throws `RangeError` on a non-integer, `NaN` or `Infinity`\n * instead of silently stringifying them — which is why non-string inputs go\n * through it. The `string` arm gets its own check instead of relying on\n * `BigInt`'s string parsing, which is far more permissive than a decimal\n * string contract should be.\n */\nexport function normalizeCredits(credits: string | number | bigint): string {\n if (typeof credits === 'string') {\n if (!DECIMAL_INTEGER_STRING.test(credits)) {\n throw new MppError(\n `credits must be a non-negative integer decimal string, got ${JSON.stringify(credits)}`,\n )\n }\n return credits\n }\n\n let normalized: string\n try {\n normalized = BigInt(credits).toString()\n } catch (error) {\n throw new MppError(\n `credits must be a non-negative integer, got ${credits}: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n if (normalized.startsWith('-')) {\n throw new MppError(`credits must be a non-negative integer, got ${credits}`)\n }\n return normalized\n}\n\n/** Deadline on an outbound MPP request so a hung backend cannot hold the\n * caller's connection open indefinitely — `middleware.ts` defers the\n * buyer's own response until settlement resolves. */\nconst REQUEST_TIMEOUT_MS = 30_000\n\n/**\n * Socket-level failures that can only happen once the request was already on\n * the wire, so on a burning call the backend may have completed the burn and\n * only the answer was lost.\n *\n * `ECONNRESET` / `EPIPE` mean the peer tore down (or we wrote into) a\n * connection that had been established; `UND_ERR_SOCKET` is undici's wrapper\n * for the same thing, and `UND_ERR_ABORTED` covers a mid-flight abort that\n * did not come through the `TimeoutError` path.\n */\nconst OUTCOME_UNKNOWN_SOCKET_CODES = new Set([\n 'ECONNRESET',\n 'EPIPE',\n 'UND_ERR_SOCKET',\n 'UND_ERR_ABORTED',\n])\n\n/**\n * Whether a `fetch` rejection is a connection that died AFTER the request was\n * written — i.e. an unknown settlement outcome rather than a definite failure.\n *\n * Deliberately an ALLOW-LIST, not a blanket promotion of every network error.\n * `ECONNREFUSED`, `ENOTFOUND` and `EAI_AGAIN` all mean the request never\n * reached anything that could burn, so reporting them as \"may have burned\"\n * would inflate a seller's records with burns that never happened — the same\n * corruption as the 4xx case one screen down, in the other direction. Any code\n * not named here keeps the definite `network_error` classification.\n *\n * Node's `fetch` surfaces the underlying socket error as `error.cause`, so the\n * code is read from there first and off the error itself as a fallback.\n */\nfunction isPostRequestSocketFailure(error: unknown): boolean {\n if (!(error instanceof Error)) return false\n // `cause` is read off a widened shape rather than `Error.cause`: the\n // package's lib target predates ES2022, so the property is not on the\n // declared `Error` type even though Node populates it.\n const { cause, code: ownCode } = error as unknown as { cause?: unknown; code?: unknown }\n const causeCode = (cause as { code?: unknown } | undefined)?.code\n return (\n (typeof causeCode === 'string' && OUTCOME_UNKNOWN_SOCKET_CODES.has(causeCode)) ||\n (typeof ownCode === 'string' && OUTCOME_UNKNOWN_SOCKET_CODES.has(ownCode))\n )\n}\n\nexport interface IssueMppChallengeParams {\n /** The Nevermined plan the credits are burned against. */\n planId: string\n /** Credits the buyer is asked to redeem. Sent as a decimal string. */\n credits: string | number | bigint\n agentId?: string\n /** The protected resource. Sealed into the challenge and re-asserted at redeem. */\n resource: string\n /** HTTP verb of that resource. Part of the same binding. */\n httpVerb: string\n /** `sha-256=<base64>` digest binding the challenge to one request body. */\n digest?: string\n description?: string\n}\n\nexport interface RedeemMppParams {\n /** The `Authorization: Payment …` value presented by the buyer. */\n credential: string\n /** Must equal the resource the challenge was issued for. */\n resource: string\n /** Must equal the verb the challenge was issued for. */\n httpVerb: string\n /** Digest of the body actually received, when the challenge bound one. */\n bodyDigest?: string\n}\n\n/**\n * A discriminated union rather than a flat `paymentReceipt: string`: a\n * settlement can fail, and `SettlePermissionsResult.success` already says\n * so, but the flat shape let `{ success: false }` with no receipt typecheck\n * — a state the seller middleware then had no way to reject at compile time\n * even though it could not safely attach a receipt header for it.\n */\nexport type MppSettleResult =\n | (SettlePermissionsResult & { success: true; paymentReceipt: string })\n | (SettlePermissionsResult & { success: false; paymentReceipt?: string })\n\nexport class MppAPI extends BasePaymentsAPI {\n static getInstance(options: PaymentOptions): MppAPI {\n return new MppAPI(options)\n }\n\n /**\n * Mints the challenge a plan-protected endpoint returns with its 402.\n *\n * Each call returns a distinct challenge even for identical inputs — the id\n * doubles as the burn idempotency key, so two requests sharing one would\n * settle as a single burn.\n */\n async issueChallenge(\n params: IssueMppChallengeParams,\n ): Promise<{ challenge: string; id: string }> {\n const { planId, credits, agentId, resource, httpVerb, digest, description } = params\n return this.post<{ challenge: string; id: string }>(API_URL_MPP_CHALLENGE, {\n planId,\n credits: normalizeCredits(credits),\n resource,\n httpVerb,\n ...(agentId && { agentId }),\n ...(digest && { digest }),\n ...(description && { description }),\n })\n }\n\n /** Runs the full credential and plan checks without burning anything. */\n async verifyCredential(params: RedeemMppParams): Promise<VerifyPermissionsResult> {\n return this.post<VerifyPermissionsResult>(API_URL_MPP_VERIFY, this.redeemBody(params))\n }\n\n /**\n * Verifies and burns through the same chokepoint an x402 settlement uses, so\n * the credits charged are identical on both protocols. Settling the same\n * credential twice burns once.\n */\n async settleCredential(params: RedeemMppParams): Promise<MppSettleResult> {\n return this.post<MppSettleResult>(API_URL_MPP_SETTLE, this.redeemBody(params), {\n burns: true,\n })\n }\n\n /**\n * Mints an access token signed under the `Nevermined-MPP` EIP-712 domain.\n *\n * Same inputs and same settlement rail as {@link X402TokenAPI.getX402AccessToken};\n * the token verifies only on the MPP routes, which is what keeps the two\n * protocols isolated even though the tokens are byte-identical on the wire.\n */\n async getMppAccessToken(\n planId: string,\n agentId?: string,\n tokenOptions?: X402TokenOptions,\n ): Promise<{ accessToken: string }> {\n const body = buildX402TokenRequestBody({\n planId,\n agentId,\n tokenOptions,\n environmentName: this.environmentName,\n })\n return this.post<{ accessToken: string }>(API_URL_MPP_CREATE_PERMISSION, body)\n }\n\n /**\n * Performs an HTTP request, paying an MPP challenge if the endpoint returns\n * one.\n *\n * The buyer needs no new plan, delegation or credential: the delegation that\n * works for x402 works here unchanged.\n *\n * `init.body`, if set, must be replayable **if the endpoint may challenge\n * the request**: a `ReadableStream` throws a typed {@link PaymentsError}\n * once a 402 challenge actually requires a retry, since the stream cannot\n * be resent. A request that is never challenged sends a stream body exactly\n * once, exactly like plain `fetch` — the `paid: false` / untouched-response\n * guarantee still holds. A `string`, `Buffer`/`ArrayBuffer`/typed array,\n * `URLSearchParams`, `FormData` or `Blob` body all work unchanged either way.\n *\n * `options.delegationConfig` must carry a `delegationId` — this call\n * refuses the deprecated inline create-on-the-fly shape (no `delegationId`)\n * that {@link X402TokenAPI.getX402AccessToken} otherwise tolerates with a\n * warning: the retry loop here can mint an access token twice per call, so\n * that shape could silently create two delegations as a side effect of\n * paying.\n *\n * @example\n * ```typescript\n * const { response, receipt } = await payments.mpp.fetch(\n * 'https://agent.example/ask',\n * { method: 'POST', body: JSON.stringify({ q: 'hello' }) },\n * { delegationConfig: { delegationId }, planId },\n * )\n * ```\n */\n async fetch(\n input: string | URL,\n init: RequestInit | undefined,\n options: MppFetchOptions,\n ): Promise<MppFetchResult> {\n if (!options?.delegationConfig?.delegationId) {\n throw PaymentsError.validation(\n 'payments.mpp.fetch requires delegationConfig.delegationId. Create a delegation first ' +\n 'with payments.delegation.createDelegation(), then pass { delegationId }. An inline ' +\n 'create-on-the-fly delegationConfig (no delegationId) is not accepted here — the retry ' +\n 'loop can mint against it twice per call, which would create two delegations.',\n )\n }\n return mppFetch(\n (planId, agentId, tokenOptions) => this.getMppAccessToken(planId, agentId, tokenOptions),\n input,\n init,\n options,\n )\n }\n\n private redeemBody(params: RedeemMppParams): Record<string, unknown> {\n const { credential, resource, httpVerb, bodyDigest } = params\n return {\n credential,\n resource,\n httpVerb,\n ...(bodyDigest && { bodyDigest }),\n }\n }\n\n /**\n * One place for the POST + error translation both surfaces share.\n *\n * `burns` marks a call where \"no answer\" is not the same as \"did not\n * happen\" — currently only `settleCredential`. For that call alone, a\n * fetch rejection caused by OUR OWN `AbortSignal.timeout()` firing (not\n * some other network failure) is raised as {@link MppSettlementOutcomeUnknownError}\n * instead of the generic `network_error` `MppError` used everywhere else,\n * so a caller can tell \"definitely nothing happened\" apart from \"the\n * backend may have already burned the credits; we just didn't hear back\".\n * `error.name === 'TimeoutError'` combined with `instanceof DOMException`\n * is the documented, empirically-confirmed shape Node's `fetch` throws\n * when an `AbortSignal.timeout()` deadline fires — as opposed to, say,\n * connection-refused, which surfaces as a plain `TypeError`.\n *\n * The same reasoning covers the other two ways a burning call can end\n * without a definite answer: an upstream 5xx/408, and a 2xx whose body\n * could not be read. All three raise {@link MppSettlementOutcomeUnknownError};\n * only a 4xx — the backend deliberately rejecting — is a definite failure.\n */\n private async post<T>(\n path: string,\n body: Record<string, unknown>,\n options: { burns?: boolean } = {},\n ): Promise<T> {\n const url = new URL(path, this.environment.backend)\n const requestOptions = {\n ...this.getBackendHTTPOptions('POST', body),\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n }\n\n let response: Response\n try {\n response = await fetch(url, requestOptions)\n } catch (error) {\n if (options.burns && error instanceof DOMException && error.name === 'TimeoutError') {\n throw new MppSettlementOutcomeUnknownError()\n }\n if (options.burns && isPostRequestSocketFailure(error)) {\n throw new MppSettlementOutcomeUnknownError(\n `The connection to the backend failed after the settle request was written: ${\n error instanceof Error ? error.message : String(error)\n }`,\n )\n }\n throw toMppError(\n 'network_error',\n `Network error during MPP request: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n\n if (!response.ok) {\n let message = `MPP request to ${path} failed`\n let code: string | undefined = `http_${response.status}`\n try {\n const errorData = await response.json()\n if (errorData.message) message = errorData.message\n if (errorData.code) code = errorData.code\n if (errorData.hint) message = `${message} — ${errorData.hint}`\n } catch {\n // Keep the default message.\n }\n // On a burning call, a 5xx (or a 408) is an UNKNOWN outcome, not a\n // failure. A 504 in particular means an intermediary gave up waiting on\n // the backend — it says nothing about whether the burn committed, which\n // is the exact condition MppSettlementOutcomeUnknownError exists for,\n // and the argument the docstring below makes for an unreadable 2xx\n // applies word for word. Classified as a definite failure, the\n // middleware logs \"settlement failed\" and skips onAfterSettle for\n // credits that may well be gone.\n //\n // Confined to 5xx/408: the backend's own rejections (BCK.MPP.0003 and\n // friends) come back as 4xx and ARE definite — reporting those as\n // unknown would corrupt the accounting in the other direction.\n if (options.burns && (response.status >= 500 || response.status === 408)) {\n throw new MppSettlementOutcomeUnknownError(\n `The backend answered ${response.status} without a definite settlement outcome: ${message}`,\n )\n }\n throw toMppError(code, message)\n }\n\n // The success path is not exempt from a malformed body: a WAF\n // interstitial, a gateway HTML page, or a truncated 2xx response would\n // otherwise raise a raw SyntaxError here — the one call site in this\n // method that was NOT already wrapped and converted to a typed error.\n try {\n return (await response.json()) as T\n } catch (error) {\n // On a burning call this is an UNKNOWN outcome, not a failure. The\n // backend already answered 2xx — the burn committed — and only the\n // body was lost (our own AbortSignal.timeout() also aborts the body\n // stream, and a mid-body reset lands here too). Reporting it as a\n // definite failure is exactly the accounting corruption\n // MppSettlementOutcomeUnknownError exists to prevent: the middleware\n // would log \"settlement failed\" and skip onAfterSettle for credits\n // that were in fact burned. If anything this case is MORE likely to\n // have burned than the pre-response timeout the error was introduced\n // for.\n if (options.burns) {\n throw new MppSettlementOutcomeUnknownError(\n `The backend answered ${response.status} but its body could not be read: ${\n error instanceof Error ? error.message : String(error)\n }`,\n )\n }\n throw toMppError(\n `http_${response.status}`,\n `MPP response from ${path} was not valid JSON: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mpp-api.js","sourceRoot":"","sources":["../../src/mpp/mpp-api.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Machine Payments Protocol (MPP) API.\n *\n * MPP is a second payment framing over the unchanged Nevermined core: the same\n * plan, the same delegation and the same credit burn as x402, negotiated with\n * MPP headers. Sellers issue a challenge and redeem a credential; buyers mint\n * an MPP-domain access token and present it as a credential.\n */\n\nimport { BasePaymentsAPI } from '../api/base-payments.js'\nimport {\n API_URL_MPP_CHALLENGE,\n API_URL_MPP_CREATE_PERMISSION,\n API_URL_MPP_SETTLE,\n API_URL_MPP_VERIFY,\n} from '../api/nvm-api.js'\nimport { PaymentsError } from '../common/payments.error.js'\nimport type { MppTokenOptions, PaymentOptions } from '../common/types.js'\nimport type { SettlePermissionsResult, VerifyPermissionsResult } from '../x402/facilitator-api.js'\nimport { buildX402TokenRequestBody } from '../x402/token-request.js'\nimport { MppError, MppSettlementOutcomeUnknownError, toMppError } from './errors.js'\nimport { mppFetch, type MppFetchOptions, type MppFetchResult } from './fetch.js'\n\n/** Only whole, non-negative decimal digits — no sign, no leading/trailing\n * whitespace, no hex/octal/binary prefix, no fractional part. `BigInt(x)`\n * silently accepts all of those for a string input (`BigInt('0x10')` is 16,\n * `BigInt('')` is 0n, `BigInt(' 5 ')` is 5n), which a decimal-string\n * contract must reject rather than accept quietly. */\nconst DECIMAL_INTEGER_STRING = /^\\d+$/\n\n/**\n * Normalizes `credits` to the exact decimal-string amount that gets sealed\n * into the challenge and burned. A bare `.toString()` on a JS `number` can\n * emit scientific notation (`1e+21`), `\"NaN\"` or `\"Infinity\"` — all\n * forwarded unvalidated into an amount this PR's own docs describe as\n * having \"no post-hoc re-pricing as there is with x402\": a corrupted amount\n * here is not correctable downstream, it IS the amount.\n *\n * `BigInt(x)` renders an integer-valued number exactly (no scientific\n * notation) and throws `RangeError` on a non-integer, `NaN` or `Infinity`\n * instead of silently stringifying them — which is why non-string inputs go\n * through it. The `string` arm gets its own check instead of relying on\n * `BigInt`'s string parsing, which is far more permissive than a decimal\n * string contract should be.\n */\nexport function normalizeCredits(credits: string | number | bigint): string {\n if (typeof credits === 'string') {\n if (!DECIMAL_INTEGER_STRING.test(credits)) {\n throw new MppError(\n `credits must be a non-negative integer decimal string, got ${JSON.stringify(credits)}`,\n )\n }\n return credits\n }\n\n let normalized: string\n try {\n normalized = BigInt(credits).toString()\n } catch (error) {\n throw new MppError(\n `credits must be a non-negative integer, got ${credits}: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n if (normalized.startsWith('-')) {\n throw new MppError(`credits must be a non-negative integer, got ${credits}`)\n }\n return normalized\n}\n\n/** Deadline on an outbound MPP request so a hung backend cannot hold the\n * caller's connection open indefinitely — `middleware.ts` defers the\n * buyer's own response until settlement resolves. */\nconst REQUEST_TIMEOUT_MS = 30_000\n\n/**\n * Socket-level failures that can only happen once the request was already on\n * the wire, so on a burning call the backend may have completed the burn and\n * only the answer was lost.\n *\n * `ECONNRESET` / `EPIPE` mean the peer tore down (or we wrote into) a\n * connection that had been established; `UND_ERR_SOCKET` is undici's wrapper\n * for the same thing, and `UND_ERR_ABORTED` covers a mid-flight abort that\n * did not come through the `TimeoutError` path.\n */\nconst OUTCOME_UNKNOWN_SOCKET_CODES = new Set([\n 'ECONNRESET',\n 'EPIPE',\n 'UND_ERR_SOCKET',\n 'UND_ERR_ABORTED',\n])\n\n/**\n * Whether a `fetch` rejection is a connection that died AFTER the request was\n * written — i.e. an unknown settlement outcome rather than a definite failure.\n *\n * Deliberately an ALLOW-LIST, not a blanket promotion of every network error.\n * `ECONNREFUSED`, `ENOTFOUND` and `EAI_AGAIN` all mean the request never\n * reached anything that could burn, so reporting them as \"may have burned\"\n * would inflate a seller's records with burns that never happened — the same\n * corruption as the 4xx case one screen down, in the other direction. Any code\n * not named here keeps the definite `network_error` classification.\n *\n * Node's `fetch` surfaces the underlying socket error as `error.cause`, so the\n * code is read from there first and off the error itself as a fallback.\n */\nfunction isPostRequestSocketFailure(error: unknown): boolean {\n if (!(error instanceof Error)) return false\n // `cause` is read off a widened shape rather than `Error.cause`: the\n // package's lib target predates ES2022, so the property is not on the\n // declared `Error` type even though Node populates it.\n const { cause, code: ownCode } = error as unknown as { cause?: unknown; code?: unknown }\n const causeCode = (cause as { code?: unknown } | undefined)?.code\n return (\n (typeof causeCode === 'string' && OUTCOME_UNKNOWN_SOCKET_CODES.has(causeCode)) ||\n (typeof ownCode === 'string' && OUTCOME_UNKNOWN_SOCKET_CODES.has(ownCode))\n )\n}\n\nexport interface IssueMppChallengeParams {\n /** The Nevermined plan the credits are burned against. */\n planId: string\n /** Credits the buyer is asked to redeem. Sent as a decimal string. */\n credits: string | number | bigint\n agentId?: string\n /** The protected resource. Sealed into the challenge and re-asserted at redeem. */\n resource: string\n /** HTTP verb of that resource. Part of the same binding. */\n httpVerb: string\n /** `sha-256=<base64>` digest binding the challenge to one request body. */\n digest?: string\n description?: string\n}\n\nexport interface RedeemMppParams {\n /** The `Authorization: Payment …` value presented by the buyer. */\n credential: string\n /** Must equal the resource the challenge was issued for. */\n resource: string\n /** Must equal the verb the challenge was issued for. */\n httpVerb: string\n /** Digest of the body actually received, when the challenge bound one. */\n bodyDigest?: string\n}\n\n/**\n * A discriminated union rather than a flat `paymentReceipt: string`: a\n * settlement can fail, and `SettlePermissionsResult.success` already says\n * so, but the flat shape let `{ success: false }` with no receipt typecheck\n * — a state the seller middleware then had no way to reject at compile time\n * even though it could not safely attach a receipt header for it.\n */\nexport type MppSettleResult =\n | (SettlePermissionsResult & { success: true; paymentReceipt: string })\n | (SettlePermissionsResult & { success: false; paymentReceipt?: string })\n\nexport class MppAPI extends BasePaymentsAPI {\n static getInstance(options: PaymentOptions): MppAPI {\n return new MppAPI(options)\n }\n\n /**\n * Mints the challenge a plan-protected endpoint returns with its 402.\n *\n * Each call returns a distinct challenge even for identical inputs — the id\n * doubles as the burn idempotency key, so two requests sharing one would\n * settle as a single burn.\n */\n async issueChallenge(\n params: IssueMppChallengeParams,\n ): Promise<{ challenge: string; id: string }> {\n const { planId, credits, agentId, resource, httpVerb, digest, description } = params\n return this.post<{ challenge: string; id: string }>(API_URL_MPP_CHALLENGE, {\n planId,\n credits: normalizeCredits(credits),\n resource,\n httpVerb,\n ...(agentId && { agentId }),\n ...(digest && { digest }),\n ...(description && { description }),\n })\n }\n\n /** Runs the full credential and plan checks without burning anything. */\n async verifyCredential(params: RedeemMppParams): Promise<VerifyPermissionsResult> {\n return this.post<VerifyPermissionsResult>(API_URL_MPP_VERIFY, this.redeemBody(params))\n }\n\n /**\n * Verifies and burns through the same chokepoint an x402 settlement uses, so\n * the credits charged are identical on both protocols. Settling the same\n * credential twice burns once.\n */\n async settleCredential(params: RedeemMppParams): Promise<MppSettleResult> {\n return this.post<MppSettleResult>(API_URL_MPP_SETTLE, this.redeemBody(params), {\n burns: true,\n })\n }\n\n /**\n * Mints an access token signed under the `Nevermined-MPP` EIP-712 domain.\n *\n * Same inputs and same settlement rail as {@link X402TokenAPI.getX402AccessToken}\n * minus `tokenVersion`; the token verifies only on the MPP routes, which is\n * what keeps the two protocols isolated.\n *\n * MPP carries **no token version** (nvm-monorepo#3266): its single-use unit is\n * the challenge, not the token, so one MPP token is presented across many\n * challenges by design. The parameter type says so, and a JavaScript caller\n * that sends one anyway is refused before the request — the backend answers\n * `BCK.MPP.0007` for any value, `2` included.\n */\n async getMppAccessToken(\n planId: string,\n agentId?: string,\n tokenOptions?: MppTokenOptions,\n ): Promise<{ accessToken: string }> {\n const body = buildX402TokenRequestBody({\n planId,\n agentId,\n tokenOptions,\n environmentName: this.environmentName,\n protocol: 'mpp',\n })\n return this.post<{ accessToken: string }>(API_URL_MPP_CREATE_PERMISSION, body)\n }\n\n /**\n * Performs an HTTP request, paying an MPP challenge if the endpoint returns\n * one.\n *\n * The buyer needs no new plan, delegation or credential: the delegation that\n * works for x402 works here unchanged.\n *\n * `init.body`, if set, must be replayable **if the endpoint may challenge\n * the request**: a `ReadableStream` throws a typed {@link PaymentsError}\n * once a 402 challenge actually requires a retry, since the stream cannot\n * be resent. A request that is never challenged sends a stream body exactly\n * once, exactly like plain `fetch` — the `paid: false` / untouched-response\n * guarantee still holds. A `string`, `Buffer`/`ArrayBuffer`/typed array,\n * `URLSearchParams`, `FormData` or `Blob` body all work unchanged either way.\n *\n * `options.delegationConfig` must carry a `delegationId` — this call\n * refuses the deprecated inline create-on-the-fly shape (no `delegationId`)\n * that {@link X402TokenAPI.getX402AccessToken} otherwise tolerates with a\n * warning: the retry loop here can mint an access token twice per call, so\n * that shape could silently create two delegations as a side effect of\n * paying.\n *\n * @example\n * ```typescript\n * const { response, receipt } = await payments.mpp.fetch(\n * 'https://agent.example/ask',\n * { method: 'POST', body: JSON.stringify({ q: 'hello' }) },\n * { delegationConfig: { delegationId }, planId },\n * )\n * ```\n */\n async fetch(\n input: string | URL,\n init: RequestInit | undefined,\n options: MppFetchOptions,\n ): Promise<MppFetchResult> {\n if (!options?.delegationConfig?.delegationId) {\n throw PaymentsError.validation(\n 'payments.mpp.fetch requires delegationConfig.delegationId. Create a delegation first ' +\n 'with payments.delegation.createDelegation(), then pass { delegationId }. An inline ' +\n 'create-on-the-fly delegationConfig (no delegationId) is not accepted here — the retry ' +\n 'loop can mint against it twice per call, which would create two delegations.',\n )\n }\n return mppFetch(\n (planId, agentId, tokenOptions) => this.getMppAccessToken(planId, agentId, tokenOptions),\n input,\n init,\n options,\n )\n }\n\n private redeemBody(params: RedeemMppParams): Record<string, unknown> {\n const { credential, resource, httpVerb, bodyDigest } = params\n return {\n credential,\n resource,\n httpVerb,\n ...(bodyDigest && { bodyDigest }),\n }\n }\n\n /**\n * One place for the POST + error translation both surfaces share.\n *\n * `burns` marks a call where \"no answer\" is not the same as \"did not\n * happen\" — currently only `settleCredential`. For that call alone, a\n * fetch rejection caused by OUR OWN `AbortSignal.timeout()` firing (not\n * some other network failure) is raised as {@link MppSettlementOutcomeUnknownError}\n * instead of the generic `network_error` `MppError` used everywhere else,\n * so a caller can tell \"definitely nothing happened\" apart from \"the\n * backend may have already burned the credits; we just didn't hear back\".\n * `error.name === 'TimeoutError'` combined with `instanceof DOMException`\n * is the documented, empirically-confirmed shape Node's `fetch` throws\n * when an `AbortSignal.timeout()` deadline fires — as opposed to, say,\n * connection-refused, which surfaces as a plain `TypeError`.\n *\n * The same reasoning covers the other two ways a burning call can end\n * without a definite answer: an upstream 5xx/408, and a 2xx whose body\n * could not be read. All three raise {@link MppSettlementOutcomeUnknownError};\n * only a 4xx — the backend deliberately rejecting — is a definite failure.\n */\n private async post<T>(\n path: string,\n body: Record<string, unknown>,\n options: { burns?: boolean } = {},\n ): Promise<T> {\n const url = new URL(path, this.environment.backend)\n const requestOptions = {\n ...this.getBackendHTTPOptions('POST', body),\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n }\n\n let response: Response\n try {\n response = await fetch(url, requestOptions)\n } catch (error) {\n if (options.burns && error instanceof DOMException && error.name === 'TimeoutError') {\n throw new MppSettlementOutcomeUnknownError()\n }\n if (options.burns && isPostRequestSocketFailure(error)) {\n throw new MppSettlementOutcomeUnknownError(\n `The connection to the backend failed after the settle request was written: ${\n error instanceof Error ? error.message : String(error)\n }`,\n )\n }\n throw toMppError(\n 'network_error',\n `Network error during MPP request: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n\n if (!response.ok) {\n let message = `MPP request to ${path} failed`\n let code: string | undefined = `http_${response.status}`\n try {\n const errorData = await response.json()\n if (errorData.message) message = errorData.message\n if (errorData.code) code = errorData.code\n if (errorData.hint) message = `${message} — ${errorData.hint}`\n } catch {\n // Keep the default message.\n }\n // On a burning call, a 5xx (or a 408) is an UNKNOWN outcome, not a\n // failure. A 504 in particular means an intermediary gave up waiting on\n // the backend — it says nothing about whether the burn committed, which\n // is the exact condition MppSettlementOutcomeUnknownError exists for,\n // and the argument the docstring below makes for an unreadable 2xx\n // applies word for word. Classified as a definite failure, the\n // middleware logs \"settlement failed\" and skips onAfterSettle for\n // credits that may well be gone.\n //\n // Confined to 5xx/408: the backend's own rejections (BCK.MPP.0003 and\n // friends) come back as 4xx and ARE definite — reporting those as\n // unknown would corrupt the accounting in the other direction.\n if (options.burns && (response.status >= 500 || response.status === 408)) {\n throw new MppSettlementOutcomeUnknownError(\n `The backend answered ${response.status} without a definite settlement outcome: ${message}`,\n )\n }\n throw toMppError(code, message)\n }\n\n // The success path is not exempt from a malformed body: a WAF\n // interstitial, a gateway HTML page, or a truncated 2xx response would\n // otherwise raise a raw SyntaxError here — the one call site in this\n // method that was NOT already wrapped and converted to a typed error.\n try {\n return (await response.json()) as T\n } catch (error) {\n // On a burning call this is an UNKNOWN outcome, not a failure. The\n // backend already answered 2xx — the burn committed — and only the\n // body was lost (our own AbortSignal.timeout() also aborts the body\n // stream, and a mid-body reset lands here too). Reporting it as a\n // definite failure is exactly the accounting corruption\n // MppSettlementOutcomeUnknownError exists to prevent: the middleware\n // would log \"settlement failed\" and skip onAfterSettle for credits\n // that were in fact burned. If anything this case is MORE likely to\n // have burned than the pre-response timeout the error was introduced\n // for.\n if (options.burns) {\n throw new MppSettlementOutcomeUnknownError(\n `The backend answered ${response.status} but its body could not be read: ${\n error instanceof Error ? error.message : String(error)\n }`,\n )\n }\n throw toMppError(\n `http_${response.status}`,\n `MPP response from ${path} was not valid JSON: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n}\n"]}
|
package/dist/payments.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type { PaymentsA2AServerOptions, PaymentsA2AServerResult } from './a2a/se
|
|
|
10
10
|
import { buildPaymentAgentCard } from './a2a/agent-card.js';
|
|
11
11
|
import * as mcpModule from './mcp/index.js';
|
|
12
12
|
import { OrganizationsAPI } from './api/organizations-api/organizations-api.js';
|
|
13
|
+
import { OrdersAPI } from './api/orders-api.js';
|
|
13
14
|
import { FacilitatorAPI } from './x402/facilitator-api.js';
|
|
14
15
|
import { X402TokenAPI } from './x402/token.js';
|
|
15
16
|
import { DelegationAPI } from './x402/delegation-api.js';
|
|
@@ -23,6 +24,7 @@ import { MppAPI } from './mpp/mpp-api.js';
|
|
|
23
24
|
*
|
|
24
25
|
* Each of these functionalities is encapsulated in its own API class:
|
|
25
26
|
* - `plans`: Manages AI Plans, including registration and ordering and retrieving plan details.
|
|
27
|
+
* - `orders`: Browser-fiat Orders — merchant-initiated, off-plan charges for an arbitrary amount.
|
|
26
28
|
* - `agents`: Handles AI Agents, including registration of AI Agents and access token generation.
|
|
27
29
|
* - `requests`: Manages requests received by AI Agents, including validation and tracking.
|
|
28
30
|
* - `observability`: Provides observability and logging utilities for AI Agents with Helicone integration
|
|
@@ -34,6 +36,7 @@ export declare class Payments extends BasePaymentsAPI {
|
|
|
34
36
|
requests: AgentRequestsAPI;
|
|
35
37
|
observability: ObservabilityAPI;
|
|
36
38
|
organizations: OrganizationsAPI;
|
|
39
|
+
orders: OrdersAPI;
|
|
37
40
|
contracts: ContractsAPI;
|
|
38
41
|
facilitator: FacilitatorAPI;
|
|
39
42
|
x402: X402TokenAPI;
|