@agentcash/router 1.12.0 → 1.14.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/dist/index.js CHANGED
@@ -20,7 +20,7 @@ var init_constants = __esm({
20
20
  BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
21
21
  BASE_USDC_DECIMALS = 6;
22
22
  ZERO_EVM_ADDRESS = "0x0000000000000000000000000000000000000000";
23
- DEFAULT_SOLANA_FACILITATOR_URL = "https://facilitator.corbits.dev";
23
+ DEFAULT_SOLANA_FACILITATOR_URL = "https://facilitator.payai.network";
24
24
  DEFAULT_TEMPO_RPC_URL = "https://rpc.tempo.xyz";
25
25
  }
26
26
  });
@@ -129,31 +129,64 @@ function buildSolanaExactOptions(accepts, price) {
129
129
  function hasSolanaAccepts(accepts) {
130
130
  return accepts.some((accept) => isSolanaNetwork(accept.network));
131
131
  }
132
- async function enrichRequirementsWithFacilitatorAccepts(facilitator, resource, requirements) {
132
+ function facilitatorBaseUrl(facilitator) {
133
133
  if (!facilitator.url) {
134
- throw new Error(`Facilitator for ${facilitator.network} is missing a URL for /accepts`);
134
+ throw new Error(`Facilitator for ${facilitator.network} is missing a URL`);
135
135
  }
136
- const authHeaders = await getAcceptsHeadersForFacilitator(facilitator);
137
- const response = await fetch(`${facilitator.url.replace(/\/+$/, "")}/accepts`, {
138
- method: "POST",
139
- headers: {
140
- ...authHeaders,
141
- "content-type": "application/json"
142
- },
143
- body: JSON.stringify({
144
- x402Version: 2,
145
- resource,
146
- accepts: requirements
147
- })
136
+ return facilitator.url.replace(/\/+$/, "");
137
+ }
138
+ function matchesSupportedKind(requirement, kind) {
139
+ const scheme = requirement.scheme ?? "exact";
140
+ return kind.network === requirement.network && (kind.scheme ?? "exact") === scheme;
141
+ }
142
+ async function fetchFacilitatorSupported(facilitator) {
143
+ const authHeaders = await getSupportedHeadersForFacilitator(facilitator);
144
+ const response = await fetch(`${facilitatorBaseUrl(facilitator)}/supported`, {
145
+ headers: authHeaders
148
146
  });
149
147
  if (!response.ok) {
150
- throw new Error(`Facilitator /accepts failed with status ${response.status}`);
148
+ throw new Error(`Facilitator /supported failed with status ${response.status}`);
151
149
  }
152
150
  const body = await response.json();
153
- if (!Array.isArray(body.accepts)) {
154
- throw new Error("Facilitator /accepts response did not include accepts");
151
+ if (!Array.isArray(body.kinds)) {
152
+ throw new Error("Facilitator /supported response did not include kinds");
153
+ }
154
+ return body.kinds;
155
+ }
156
+ function enrichRequirementFromKind(requirement, kinds) {
157
+ const kind = kinds.find((candidate) => matchesSupportedKind(requirement, candidate));
158
+ if (!kind) {
159
+ throw new Error(
160
+ `Facilitator /supported has no kind for ${requirement.scheme} on ${requirement.network}`
161
+ );
162
+ }
163
+ const feePayer = kind.extra?.feePayer;
164
+ if (!feePayer) {
165
+ throw new Error(
166
+ `Facilitator /supported kind for ${requirement.network} is missing extra.feePayer`
167
+ );
155
168
  }
156
- return body.accepts;
169
+ const requirementExtra = requirement.extra ?? {};
170
+ const kindExtra = kind.extra ?? {};
171
+ const requirementFeatures = requirementExtra.features ?? {};
172
+ const kindFeatures = kindExtra.features ?? {};
173
+ return {
174
+ ...requirement,
175
+ ...kind.asset && !requirement.asset ? { asset: kind.asset } : {},
176
+ extra: {
177
+ ...requirementExtra,
178
+ ...kindExtra,
179
+ features: {
180
+ ...requirementFeatures,
181
+ ...kindFeatures,
182
+ xSettlementAccountSupported: true
183
+ }
184
+ }
185
+ };
186
+ }
187
+ async function enrichRequirementsFromFacilitatorSupported(facilitator, requirements) {
188
+ const kinds = await fetchFacilitatorSupported(facilitator);
189
+ return requirements.map((requirement) => enrichRequirementFromKind(requirement, kinds));
157
190
  }
158
191
  function isSolanaRequirement(requirement) {
159
192
  return isSolanaNetwork(requirement.network);
@@ -210,10 +243,7 @@ function getFacilitatorForRequirement(facilitatorsByNetwork, requirement) {
210
243
  function sameResolvedX402Facilitator(a, b) {
211
244
  return sameFacilitatorConfig(a.config, b.config);
212
245
  }
213
- async function getAcceptsHeadersForFacilitator(facilitator) {
214
- if (facilitator.config.createAcceptsHeaders) {
215
- return facilitator.config.createAcceptsHeaders();
216
- }
246
+ async function getSupportedHeadersForFacilitator(facilitator) {
217
247
  if (facilitator.config.createAuthHeaders) {
218
248
  const headers = await facilitator.config.createAuthHeaders();
219
249
  return headers.supported;
@@ -235,7 +265,7 @@ function getNetworkFamily(network) {
235
265
  return null;
236
266
  }
237
267
  function sameFacilitatorConfig(a, b) {
238
- return a.url === b.url && a.createAuthHeaders === b.createAuthHeaders && a.createAcceptsHeaders === b.createAcceptsHeaders;
268
+ return a.url === b.url && a.createAuthHeaders === b.createAuthHeaders;
239
269
  }
240
270
  var init_facilitators = __esm({
241
271
  "src/protocols/x402/facilitators.ts"() {
@@ -835,6 +865,13 @@ var HttpError = class extends Error {
835
865
  this.name = "HttpError";
836
866
  }
837
867
  };
868
+ var RouteDefinitionError = class extends Error {
869
+ constructor(route, detail) {
870
+ super(`route '${route}': ${detail}`);
871
+ this.route = route;
872
+ this.name = "RouteDefinitionError";
873
+ }
874
+ };
838
875
 
839
876
  // src/auth/agent-identity.ts
840
877
  var PROTOCOL_VERSION = 1;
@@ -1362,6 +1399,23 @@ function multiplyDecimal(decimal, factor) {
1362
1399
  return fracPart ? `${intPart}.${fracPart}` : intPart;
1363
1400
  }
1364
1401
 
1402
+ // src/protocols/detect.ts
1403
+ function hasX402Payment(request) {
1404
+ return Boolean(
1405
+ request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)
1406
+ );
1407
+ }
1408
+ function hasMppPayment(request) {
1409
+ const auth = request.headers.get(HEADERS.AUTHORIZATION);
1410
+ return Boolean(auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT));
1411
+ }
1412
+ function detectProtocol(request) {
1413
+ if (hasX402Payment(request)) return "x402";
1414
+ if (hasMppPayment(request)) return "mpp";
1415
+ if (request.headers.get(HEADERS.SIWX)) return "siwx";
1416
+ return null;
1417
+ }
1418
+
1365
1419
  // src/protocols/mpp/credential.ts
1366
1420
  import { Credential } from "mppx";
1367
1421
  import { getAddress, isAddress } from "viem";
@@ -1728,10 +1782,7 @@ function settleHashMode(args) {
1728
1782
  // src/protocols/mpp/strategy.ts
1729
1783
  var mppStrategy = {
1730
1784
  protocol: "mpp",
1731
- detects(request) {
1732
- const auth = request.headers.get(HEADERS.AUTHORIZATION);
1733
- return Boolean(auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT));
1734
- },
1785
+ detects: hasMppPayment,
1735
1786
  preflight(request, _routeEntry) {
1736
1787
  const info = readMppCredential(request);
1737
1788
  if (!info?.sessionAction) return null;
@@ -1936,15 +1987,14 @@ async function buildChallengeRequirements(server, request, price, accepts, resou
1936
1987
  function needsFacilitatorEnrichment(accepts) {
1937
1988
  return hasSolanaAccepts(accepts);
1938
1989
  }
1939
- async function enrichGroup(group, resource) {
1940
- const accepted = await enrichRequirementsWithFacilitatorAccepts(
1990
+ async function enrichGroup(group) {
1991
+ const accepted = await enrichRequirementsFromFacilitatorSupported(
1941
1992
  group.facilitator,
1942
- resource,
1943
1993
  group.items.map(({ requirement }) => requirement)
1944
1994
  );
1945
1995
  if (accepted.length !== group.items.length) {
1946
1996
  throw new Error(
1947
- `Facilitator /accepts returned ${accepted.length} requirements for ${group.items.length} inputs on ${group.facilitator.url ?? group.facilitator.network}`
1997
+ `Facilitator /supported enrichment returned ${accepted.length} requirements for ${group.items.length} inputs on ${group.facilitator.url ?? group.facilitator.network}`
1948
1998
  );
1949
1999
  }
1950
2000
  return accepted;
@@ -1955,13 +2005,13 @@ async function enrichChallengeRequirements(requirements, resource, facilitatorsB
1955
2005
  const results = await Promise.all(
1956
2006
  groups.map(async (group) => {
1957
2007
  try {
1958
- return { success: true, group, accepted: await enrichGroup(group, resource) };
2008
+ return { success: true, group, accepted: await enrichGroup(group) };
1959
2009
  } catch (err) {
1960
2010
  const label = group.facilitator.url ?? group.facilitator.network;
1961
2011
  const reason = err instanceof Error ? err.message : String(err);
1962
2012
  report?.(
1963
2013
  "warn",
1964
- `${label} /accepts failed, dropping ${group.items.length} requirement(s): ${reason}`
2014
+ `${label} /supported enrichment failed, dropping ${group.items.length} requirement(s): ${reason}`
1965
2015
  );
1966
2016
  return { success: false, group };
1967
2017
  }
@@ -2166,11 +2216,7 @@ function formatVerifyFailureMessage(failure) {
2166
2216
  }
2167
2217
  var x402Strategy = {
2168
2218
  protocol: "x402",
2169
- detects(request) {
2170
- return Boolean(
2171
- request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)
2172
- );
2173
- },
2219
+ detects: hasX402Payment,
2174
2220
  verify: (args) => verifyX402(args),
2175
2221
  settle: (args) => settleX402(args),
2176
2222
  buildChallenge: (args) => buildX402ChallengeContribution(args)
@@ -2317,21 +2363,6 @@ function getAllowedStrategies(allowed) {
2317
2363
  return allowed.map((name) => STRATEGIES[name]);
2318
2364
  }
2319
2365
 
2320
- // src/protocols/detect.ts
2321
- function detectProtocol(request) {
2322
- if (request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)) {
2323
- return "x402";
2324
- }
2325
- const auth = request.headers.get(HEADERS.AUTHORIZATION);
2326
- if (auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT)) {
2327
- return "mpp";
2328
- }
2329
- if (request.headers.get(HEADERS.SIWX)) {
2330
- return "siwx";
2331
- }
2332
- return null;
2333
- }
2334
-
2335
2366
  // src/pipeline/flows/api-key-only.ts
2336
2367
  async function runApiKeyOnlyFlow(ctx) {
2337
2368
  if (!ctx.routeEntry.apiKeyResolver) {
@@ -2654,6 +2685,61 @@ async function buildChallengeResponse(ctx, pricing, body, failure) {
2654
2685
  return response;
2655
2686
  }
2656
2687
 
2688
+ // src/pipeline/flows/paid-preamble.ts
2689
+ async function runPaidPreamble(ctx) {
2690
+ const { request, routeEntry, deps, report } = ctx;
2691
+ const apiKeyGate = await runApiKeyGate(ctx);
2692
+ if (!apiKeyGate.ok) return { done: true, response: apiKeyGate.response };
2693
+ const { account } = apiKeyGate;
2694
+ const pricing = selectPricing(routeEntry.pricing, {
2695
+ alert: report,
2696
+ maxPrice: routeEntry.maxPrice,
2697
+ minPrice: routeEntry.minPrice,
2698
+ route: routeEntry.key
2699
+ });
2700
+ const incomingStrategy = selectIncomingStrategy(request, routeEntry.protocols);
2701
+ const earlyResolution = await resolveEarlyBody({ ctx, pricing, incomingStrategy });
2702
+ if (!earlyResolution.ok) return { done: true, response: earlyResolution.response };
2703
+ const { earlyBody } = earlyResolution;
2704
+ const siwxFastPath = await trySiwxFastPath(ctx, account);
2705
+ if (siwxFastPath) return { done: true, response: siwxFastPath };
2706
+ if (!incomingStrategy) {
2707
+ const initError = protocolInitError(routeEntry, deps);
2708
+ if (initError) return { done: true, response: fail(ctx, 500, initError) };
2709
+ return { done: true, response: await buildChallengeResponse(ctx, pricing, earlyBody) };
2710
+ }
2711
+ return { done: false, account, pricing, incomingStrategy, earlyBody };
2712
+ }
2713
+ async function runPaidVerify(args) {
2714
+ const { ctx, strategy, pricing, parsedBody, price } = args;
2715
+ const { request, routeEntry, deps, report } = ctx;
2716
+ const verifyOutcome = await strategy.verify({
2717
+ request,
2718
+ body: parsedBody,
2719
+ price,
2720
+ routeEntry,
2721
+ deps,
2722
+ report
2723
+ });
2724
+ if (verifyOutcome.ok === false) {
2725
+ if (verifyOutcome.kind === "config") {
2726
+ return { ok: false, response: fail(ctx, 500, verifyOutcome.message, parsedBody) };
2727
+ }
2728
+ return {
2729
+ ok: false,
2730
+ response: await buildChallengeResponse(ctx, pricing, parsedBody, verifyOutcome.failure)
2731
+ };
2732
+ }
2733
+ ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
2734
+ firePaymentVerified(ctx, {
2735
+ protocol: strategy.protocol,
2736
+ payer: verifyOutcome.wallet,
2737
+ amount: price,
2738
+ network: verifyOutcome.payment.network
2739
+ });
2740
+ return { ok: true, verifyOutcome };
2741
+ }
2742
+
2657
2743
  // src/pipeline/flows/dynamic/dynamic-body-and-price.ts
2658
2744
  async function resolveDynamicBodyAndPrice(args) {
2659
2745
  const { ctx, pricing, skipBody } = args;
@@ -3020,27 +3106,10 @@ async function runDynamicStreamFlow(args) {
3020
3106
 
3021
3107
  // src/pipeline/flows/dynamic/dynamic-paid.ts
3022
3108
  async function runDynamicPaidFlow(ctx) {
3023
- const { request, routeEntry, deps, report } = ctx;
3024
- const apiKeyGate = await runApiKeyGate(ctx);
3025
- if (!apiKeyGate.ok) return apiKeyGate.response;
3026
- const { account } = apiKeyGate;
3027
- const pricing = selectPricing(routeEntry.pricing, {
3028
- alert: report,
3029
- maxPrice: routeEntry.maxPrice,
3030
- minPrice: routeEntry.minPrice,
3031
- route: routeEntry.key
3032
- });
3033
- const incomingStrategy = selectIncomingStrategy(request, routeEntry.protocols);
3034
- const earlyResolution = await resolveEarlyBody({ ctx, pricing, incomingStrategy });
3035
- if (!earlyResolution.ok) return earlyResolution.response;
3036
- const { earlyBody } = earlyResolution;
3037
- const siwxFastPath = await trySiwxFastPath(ctx, account);
3038
- if (siwxFastPath) return siwxFastPath;
3039
- if (!incomingStrategy) {
3040
- const initError = protocolInitError(routeEntry, deps);
3041
- if (initError) return fail(ctx, 500, initError);
3042
- return buildChallengeResponse(ctx, pricing, earlyBody);
3043
- }
3109
+ const { request, routeEntry } = ctx;
3110
+ const preamble = await runPaidPreamble(ctx);
3111
+ if (preamble.done) return preamble.response;
3112
+ const { account, pricing, incomingStrategy } = preamble;
3044
3113
  const { skipBody, skipHandler } = resolveDynamicPreflight(incomingStrategy, request, routeEntry);
3045
3114
  if (skipHandler) {
3046
3115
  return runDynamicChannelMgmtFlow({
@@ -3054,27 +3123,15 @@ async function runDynamicPaidFlow(ctx) {
3054
3123
  const bodyAndPrice = await resolveDynamicBodyAndPrice({ ctx, pricing, skipBody });
3055
3124
  if (!bodyAndPrice.ok) return bodyAndPrice.response;
3056
3125
  const { parsedBody, price } = bodyAndPrice;
3057
- const verifyOutcome = await incomingStrategy.verify({
3058
- request,
3059
- body: parsedBody,
3060
- price,
3061
- routeEntry,
3062
- deps,
3063
- report
3064
- });
3065
- if (verifyOutcome.ok === false) {
3066
- if (verifyOutcome.kind === "config") {
3067
- return fail(ctx, 500, verifyOutcome.message, parsedBody);
3068
- }
3069
- return buildChallengeResponse(ctx, pricing, parsedBody, verifyOutcome.failure);
3070
- }
3071
- ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
3072
- firePaymentVerified(ctx, {
3073
- protocol: incomingStrategy.protocol,
3074
- payer: verifyOutcome.wallet,
3075
- amount: price,
3076
- network: verifyOutcome.payment.network
3126
+ const verify = await runPaidVerify({
3127
+ ctx,
3128
+ strategy: incomingStrategy,
3129
+ pricing,
3130
+ parsedBody,
3131
+ price
3077
3132
  });
3133
+ if (!verify.ok) return verify.response;
3134
+ const { verifyOutcome } = verify;
3078
3135
  const result = await invokeDynamic(ctx, verifyOutcome, account, parsedBody);
3079
3136
  switch (result.kind) {
3080
3137
  case "stream":
@@ -3206,51 +3263,21 @@ function failureFromCause2(cause) {
3206
3263
 
3207
3264
  // src/pipeline/flows/static/static-paid.ts
3208
3265
  async function runStaticPaidFlow(ctx) {
3209
- const { request, routeEntry, deps, report } = ctx;
3210
- const apiKeyGate = await runApiKeyGate(ctx);
3211
- if (!apiKeyGate.ok) return apiKeyGate.response;
3212
- const { account } = apiKeyGate;
3213
- const pricing = selectPricing(routeEntry.pricing, {
3214
- alert: report,
3215
- maxPrice: routeEntry.maxPrice,
3216
- minPrice: routeEntry.minPrice,
3217
- route: routeEntry.key
3218
- });
3219
- const incomingStrategy = selectIncomingStrategy(request, routeEntry.protocols);
3220
- const earlyResolution = await resolveEarlyBody({ ctx, pricing, incomingStrategy });
3221
- if (!earlyResolution.ok) return earlyResolution.response;
3222
- const { earlyBody } = earlyResolution;
3223
- const siwxFastPath = await trySiwxFastPath(ctx, account);
3224
- if (siwxFastPath) return siwxFastPath;
3225
- if (!incomingStrategy) {
3226
- const initError = protocolInitError(routeEntry, deps);
3227
- if (initError) return fail(ctx, 500, initError);
3228
- return buildChallengeResponse(ctx, pricing, earlyBody);
3229
- }
3266
+ const preamble = await runPaidPreamble(ctx);
3267
+ if (preamble.done) return preamble.response;
3268
+ const { account, pricing, incomingStrategy } = preamble;
3230
3269
  const bodyAndPrice = await resolveStaticBodyAndPrice({ ctx, pricing });
3231
3270
  if (!bodyAndPrice.ok) return bodyAndPrice.response;
3232
3271
  const { parsedBody, price } = bodyAndPrice;
3233
- const verifyOutcome = await incomingStrategy.verify({
3234
- request,
3235
- body: parsedBody,
3236
- price,
3237
- routeEntry,
3238
- deps,
3239
- report
3240
- });
3241
- if (verifyOutcome.ok === false) {
3242
- if (verifyOutcome.kind === "config") {
3243
- return fail(ctx, 500, verifyOutcome.message, parsedBody);
3244
- }
3245
- return buildChallengeResponse(ctx, pricing, parsedBody, verifyOutcome.failure);
3246
- }
3247
- ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
3248
- firePaymentVerified(ctx, {
3249
- protocol: incomingStrategy.protocol,
3250
- payer: verifyOutcome.wallet,
3251
- amount: price,
3252
- network: verifyOutcome.payment.network
3272
+ const verify = await runPaidVerify({
3273
+ ctx,
3274
+ strategy: incomingStrategy,
3275
+ pricing,
3276
+ parsedBody,
3277
+ price
3253
3278
  });
3279
+ if (!verify.ok) return verify.response;
3280
+ const { verifyOutcome } = verify;
3254
3281
  const result = await invokePaidStatic(
3255
3282
  ctx,
3256
3283
  verifyOutcome.wallet,
@@ -3742,55 +3769,35 @@ var RouteBuilder = class _RouteBuilder {
3742
3769
  return next;
3743
3770
  }
3744
3771
  paid(arg, options) {
3745
- return this.applyPaid(normalizePaidArg(this.#s.key, arg, options), "paid");
3772
+ const self = this;
3773
+ return self.applyPaid(normalizePaidArg(self.#s.key, arg, options), "paid");
3746
3774
  }
3747
- /**
3748
- * x402-only handler-computed billing. The handler receives `charge(amount)`
3749
- * and the request settles once for the accumulated total, capped at
3750
- * `maxPrice`. Requires an `'upto'` accept on at least one configured network.
3751
- * Pass a bare string as sugar for `{ maxPrice }`.
3752
- *
3753
- * @example
3754
- * ```ts
3755
- * router.route('llm')
3756
- * .upTo('0.05')
3757
- * .body(schema)
3758
- * .handler(async ({ body, charge }) => { await charge('0.001'); ... });
3759
- * ```
3760
- */
3761
3775
  upTo(arg) {
3762
- return this.applyPaid(normalizeUpToArg(this.#s.key, arg), "upTo");
3776
+ const self = this;
3777
+ return self.applyPaid(normalizeUpToArg(self.#s.key, arg), "upTo");
3763
3778
  }
3764
- /**
3765
- * MPP-only per-tick billing. `.handler()` bills exactly `tickCost`;
3766
- * `.stream()` calls `charge()` (no-arg) per yield, settling per tick up to
3767
- * `maxPrice`. Requires `RouterConfig.mpp.session`.
3768
- *
3769
- * @example
3770
- * ```ts
3771
- * router.route('llm/stream')
3772
- * .metered({ tickCost: '0.0001', maxPrice: '0.05', unitType: 'token' })
3773
- * .stream(async function* ({ charge }) { await charge(); yield 'hi'; });
3774
- * ```
3775
- */
3776
3779
  metered(options) {
3777
- return this.applyPaid(normalizeMeteredArg(this.#s.key, options), "metered");
3780
+ const self = this;
3781
+ return self.applyPaid(normalizeMeteredArg(self.#s.key, options), "metered");
3778
3782
  }
3779
3783
  applyPaid(normalized, method) {
3780
3784
  const { pricing, resolvedOptions, billing, tickCost, unitType, maxPrice } = normalized;
3781
3785
  if (this.#s.authMode === "unprotected") {
3782
- throw new Error(
3783
- `route '${this.#s.key}': Cannot combine .unprotected() and .${method}() on the same route.`
3786
+ throw new RouteDefinitionError(
3787
+ this.#s.key,
3788
+ `Cannot combine .unprotected() and .${method}() on the same route.`
3784
3789
  );
3785
3790
  }
3786
3791
  if (this.#s.pricing !== void 0) {
3787
- throw new Error(
3788
- `route '${this.#s.key}': Cannot combine .paid(), .upTo(), and .metered() \u2014 pick one pricing mode.`
3792
+ throw new RouteDefinitionError(
3793
+ this.#s.key,
3794
+ `Cannot combine .paid(), .upTo(), and .metered() \u2014 pick one pricing mode.`
3789
3795
  );
3790
3796
  }
3791
3797
  if (this.#s.siwxEnabled && billing === "metered") {
3792
- throw new Error(
3793
- `route '${this.#s.key}': Cannot combine .siwx() and .metered() \u2014 per-tick MPP billing has no entitlement model. Use .paid() or .upTo() with .siwx(), or drop .siwx() for metered routes.`
3798
+ throw new RouteDefinitionError(
3799
+ this.#s.key,
3800
+ `Cannot combine .siwx() and .metered() \u2014 per-tick MPP billing has no entitlement model. Use .paid() or .upTo() with .siwx(), or drop .siwx() for metered routes.`
3794
3801
  );
3795
3802
  }
3796
3803
  const next = this.fork();
@@ -3798,15 +3805,17 @@ var RouteBuilder = class _RouteBuilder {
3798
3805
  next.#s.pricing = pricing;
3799
3806
  if (billing === "upto") {
3800
3807
  if (resolvedOptions.protocols?.some((p) => p !== "x402")) {
3801
- throw new Error(
3802
- `route '${this.#s.key}': .upTo() is x402-only \u2014 remove the conflicting protocols override.`
3808
+ throw new RouteDefinitionError(
3809
+ this.#s.key,
3810
+ `.upTo() is x402-only \u2014 remove the conflicting protocols override.`
3803
3811
  );
3804
3812
  }
3805
3813
  next.#s.protocols = ["x402"];
3806
3814
  } else if (billing === "metered") {
3807
3815
  if (resolvedOptions.protocols?.some((p) => p !== "mpp")) {
3808
- throw new Error(
3809
- `route '${this.#s.key}': .metered() is MPP-only \u2014 remove the conflicting protocols override.`
3816
+ throw new RouteDefinitionError(
3817
+ this.#s.key,
3818
+ `.metered() is MPP-only \u2014 remove the conflicting protocols override.`
3810
3819
  );
3811
3820
  }
3812
3821
  next.#s.protocols = ["mpp"];
@@ -3830,73 +3839,69 @@ var RouteBuilder = class _RouteBuilder {
3830
3839
  if (typeof pricing === "object" && "tiers" in pricing) {
3831
3840
  for (const [tierKey, tierConfig] of Object.entries(pricing.tiers)) {
3832
3841
  if (!tierKey) {
3833
- throw new Error(`route '${this.#s.key}': tier key cannot be empty`);
3842
+ throw new RouteDefinitionError(this.#s.key, `tier key cannot be empty`);
3834
3843
  }
3835
3844
  if (!isPositiveDecimal(tierConfig.price)) {
3836
- throw new Error(
3837
- `route '${this.#s.key}': tier '${tierKey}' price '${tierConfig.price}' must be a positive decimal string`
3845
+ throw new RouteDefinitionError(
3846
+ this.#s.key,
3847
+ `tier '${tierKey}' price '${tierConfig.price}' must be a positive decimal string`
3838
3848
  );
3839
3849
  }
3840
3850
  }
3841
3851
  }
3842
3852
  if (billing === "exact" && typeof pricing === "string" && !isPositiveDecimal(pricing)) {
3843
- throw new Error(
3844
- `route '${this.#s.key}': price '${pricing}' must be a positive decimal string`
3853
+ throw new RouteDefinitionError(
3854
+ this.#s.key,
3855
+ `price '${pricing}' must be a positive decimal string`
3845
3856
  );
3846
3857
  }
3847
3858
  if (typeof pricing === "function" && next.#s.maxPrice === void 0) {
3848
- throw new Error(
3849
- `route '${this.#s.key}': dynamic pricing requires maxPrice \u2014 without it, bare probes would advertise a $0 challenge`
3859
+ throw new RouteDefinitionError(
3860
+ this.#s.key,
3861
+ `dynamic pricing requires maxPrice \u2014 without it, bare probes would advertise a $0 challenge`
3850
3862
  );
3851
3863
  }
3852
3864
  if (next.#s.maxPrice !== void 0 && !isPositiveDecimal(next.#s.maxPrice)) {
3853
- throw new Error(
3854
- `route '${this.#s.key}': maxPrice '${next.#s.maxPrice}' must be a positive decimal string`
3865
+ throw new RouteDefinitionError(
3866
+ this.#s.key,
3867
+ `maxPrice '${next.#s.maxPrice}' must be a positive decimal string`
3855
3868
  );
3856
3869
  }
3857
3870
  if (next.#s.minPrice !== void 0 && !isPositiveDecimal(next.#s.minPrice)) {
3858
- throw new Error(
3859
- `route '${this.#s.key}': minPrice '${next.#s.minPrice}' must be a positive decimal string`
3871
+ throw new RouteDefinitionError(
3872
+ this.#s.key,
3873
+ `minPrice '${next.#s.minPrice}' must be a positive decimal string`
3860
3874
  );
3861
3875
  }
3862
3876
  if (next.#s.tickCost !== void 0 && !isPositiveDecimal(next.#s.tickCost)) {
3863
- throw new Error(
3864
- `route '${this.#s.key}': tickCost '${next.#s.tickCost}' must be a positive decimal string`
3877
+ throw new RouteDefinitionError(
3878
+ this.#s.key,
3879
+ `tickCost '${next.#s.tickCost}' must be a positive decimal string`
3865
3880
  );
3866
3881
  }
3867
3882
  return next;
3868
3883
  }
3869
- /**
3870
- * Require Sign-In-with-X wallet identity on this route — clients prove
3871
- * control of a wallet via a signed challenge. Composes with `.paid()` and
3872
- * `.upTo()` for pay-once-then-replay: the first request settles normally,
3873
- * subsequent requests with a valid SIWX signature for the same wallet skip
3874
- * payment (on `.upTo()`, `charge(amount)` becomes a no-op on the replay).
3875
- * Mutually exclusive with `.metered()`.
3876
- *
3877
- * @example
3878
- * ```ts
3879
- * router.route('profile').siwx().handler(async ({ wallet }) => getProfile(wallet));
3880
- * router.route('inbox').paid('0.01').siwx().handler(async ({ wallet }) => getInbox(wallet));
3881
- * ```
3882
- */
3883
3884
  siwx() {
3884
- if (this.#s.authMode === "unprotected") {
3885
- throw new Error(
3886
- `route '${this.#s.key}': Cannot combine .unprotected() and .siwx() on the same route.`
3885
+ const self = this;
3886
+ if (self.#s.authMode === "unprotected") {
3887
+ throw new RouteDefinitionError(
3888
+ self.#s.key,
3889
+ `Cannot combine .unprotected() and .siwx() on the same route.`
3887
3890
  );
3888
3891
  }
3889
- if (this.#s.apiKeyResolver) {
3890
- throw new Error(
3891
- `route '${this.#s.key}': Combining .siwx() and .apiKey() is not supported on the same route.`
3892
+ if (self.#s.apiKeyResolver) {
3893
+ throw new RouteDefinitionError(
3894
+ self.#s.key,
3895
+ `Combining .siwx() and .apiKey() is not supported on the same route.`
3892
3896
  );
3893
3897
  }
3894
- if (this.#s.billing === "metered") {
3895
- throw new Error(
3896
- `route '${this.#s.key}': Cannot combine .metered() and .siwx() \u2014 per-tick MPP billing has no entitlement model. Use .paid() or .upTo() with .siwx(), or drop .siwx() for metered routes.`
3898
+ if (self.#s.billing === "metered") {
3899
+ throw new RouteDefinitionError(
3900
+ self.#s.key,
3901
+ `Cannot combine .metered() and .siwx() \u2014 per-tick MPP billing has no entitlement model. Use .paid() or .upTo() with .siwx(), or drop .siwx() for metered routes.`
3897
3902
  );
3898
3903
  }
3899
- const next = this.fork();
3904
+ const next = self.fork();
3900
3905
  next.#s.siwxEnabled = true;
3901
3906
  if (next.#s.authMode === "paid" || next.#s.pricing) {
3902
3907
  next.#s.authMode = "paid";
@@ -3907,59 +3912,48 @@ var RouteBuilder = class _RouteBuilder {
3907
3912
  next.#s.protocols = [];
3908
3913
  return next;
3909
3914
  }
3910
- /**
3911
- * Require an `X-API-Key` header (or `Authorization: Bearer <key>`); the
3912
- * resolver returns the account record, or `null` for 401. Composes with
3913
- * `.paid()` — key is checked first, payment second.
3914
- *
3915
- * @example
3916
- * ```ts
3917
- * router
3918
- * .route('admin/users')
3919
- * .apiKey(async (key) => db.admin.findByKey(key))
3920
- * .handler(async ({ account }) => db.user.list(account.orgId));
3921
- * ```
3922
- */
3923
3915
  apiKey(resolver) {
3924
- if (this.#s.siwxEnabled) {
3925
- throw new Error(
3926
- `route '${this.#s.key}': Combining .apiKey() and .siwx() is not supported on the same route.`
3916
+ const self = this;
3917
+ if (self.#s.authMode === "unprotected") {
3918
+ throw new RouteDefinitionError(
3919
+ self.#s.key,
3920
+ `Cannot combine .unprotected() and .apiKey() on the same route.`
3927
3921
  );
3928
3922
  }
3929
- const next = this.fork();
3923
+ if (self.#s.siwxEnabled) {
3924
+ throw new RouteDefinitionError(
3925
+ self.#s.key,
3926
+ `Combining .apiKey() and .siwx() is not supported on the same route.`
3927
+ );
3928
+ }
3929
+ const next = self.fork();
3930
3930
  next.#s.authMode = "apiKey";
3931
3931
  next.#s.apiKeyResolver = resolver;
3932
3932
  return next;
3933
3933
  }
3934
- /**
3935
- * Mark the route as public — no auth, no payment, no SIWX. The handler
3936
- * receives `null` for `wallet`, `payment`, and `account`.
3937
- *
3938
- * @example
3939
- * ```ts
3940
- * router.route('health').unprotected().handler(async () => ({ status: 'ok' }));
3941
- * ```
3942
- */
3943
3934
  unprotected() {
3944
- if (this.#s.authMode && this.#s.authMode !== "unprotected") {
3945
- throw new Error(
3946
- `route '${this.#s.key}': Cannot combine .unprotected() and .${this.#s.authMode}() on the same route.`
3935
+ const self = this;
3936
+ if (self.#s.authMode && self.#s.authMode !== "unprotected") {
3937
+ throw new RouteDefinitionError(
3938
+ self.#s.key,
3939
+ `Cannot combine .unprotected() and .${self.#s.authMode}() on the same route.`
3947
3940
  );
3948
3941
  }
3949
- if (this.#s.pricing) {
3950
- throw new Error(
3951
- `route '${this.#s.key}': Cannot combine .unprotected() and .paid() on the same route.`
3942
+ if (self.#s.pricing) {
3943
+ throw new RouteDefinitionError(
3944
+ self.#s.key,
3945
+ `Cannot combine .unprotected() and .paid() on the same route.`
3952
3946
  );
3953
3947
  }
3954
- const next = this.fork();
3948
+ const next = self.fork();
3955
3949
  next.#s.authMode = "unprotected";
3956
3950
  next.#s.protocols = [];
3957
3951
  return next;
3958
3952
  }
3959
3953
  /**
3960
3954
  * Tag the route with an upstream provider for discovery and provider-side
3961
- * monitoring. The provider name and config surface in `well-known` and
3962
- * OpenAPI output.
3955
+ * monitoring. The provider name and config surface in OpenAPI discovery
3956
+ * output.
3963
3957
  *
3964
3958
  * @example
3965
3959
  * ```ts
@@ -4056,8 +4050,8 @@ var RouteBuilder = class _RouteBuilder {
4056
4050
  return next;
4057
4051
  }
4058
4052
  /**
4059
- * Set a human-readable summary of the route. Surfaces in OpenAPI,
4060
- * `well-known`, and `llms.txt` discovery output.
4053
+ * Set a human-readable summary of the route. Surfaces in OpenAPI and
4054
+ * `llms.txt` discovery output.
4061
4055
  *
4062
4056
  * @example
4063
4057
  * ```ts
@@ -4193,38 +4187,46 @@ var RouteBuilder = class _RouteBuilder {
4193
4187
  }
4194
4188
  register(handlerFn, streaming) {
4195
4189
  if (!this.#s.authMode) {
4196
- throw new Error(
4197
- `route '${this.#s.key}': Select an auth mode: .paid(pricing), .upTo(maxPrice), .metered(options), .siwx(), .apiKey(resolver), or .unprotected()`
4190
+ throw new RouteDefinitionError(
4191
+ this.#s.key,
4192
+ `Select an auth mode: .paid(pricing), .upTo(maxPrice), .metered(options), .siwx(), .apiKey(resolver), or .unprotected()`
4198
4193
  );
4199
4194
  }
4200
4195
  if (this.#s.validateFn && !this.#s.bodySchema) {
4201
- throw new Error(
4202
- `route '${this.#s.key}': .validate() requires .body() \u2014 validation runs on parsed body`
4196
+ throw new RouteDefinitionError(
4197
+ this.#s.key,
4198
+ `.validate() requires .body() \u2014 validation runs on parsed body`
4203
4199
  );
4204
4200
  }
4205
4201
  if (this.#s.settlement && !this.#s.pricing) {
4206
- throw new Error(`route '${this.#s.key}': .settlement() requires a paid route`);
4202
+ throw new RouteDefinitionError(this.#s.key, `.settlement() requires a paid route`);
4207
4203
  }
4208
4204
  if (this.#s.mppInfo?.settleBeforeHandler) {
4209
4205
  if (!this.#s.pricing) {
4210
- throw new Error(`route '${this.#s.key}': mpp.settleBeforeHandler requires a paid route`);
4206
+ throw new RouteDefinitionError(
4207
+ this.#s.key,
4208
+ `mpp.settleBeforeHandler requires a paid route`
4209
+ );
4211
4210
  }
4212
4211
  if (this.#s.billing !== "exact") {
4213
- throw new Error(
4214
- `route '${this.#s.key}': mpp.settleBeforeHandler is only supported on .paid() routes`
4212
+ throw new RouteDefinitionError(
4213
+ this.#s.key,
4214
+ `mpp.settleBeforeHandler is only supported on .paid() routes`
4215
4215
  );
4216
4216
  }
4217
4217
  if (this.#s.settlement?.beforeSettle) {
4218
- throw new Error(
4219
- `route '${this.#s.key}': mpp.settleBeforeHandler is incompatible with .settlement({ beforeSettle }) \u2014 MPP payment is already broadcast before the handler runs`
4218
+ throw new RouteDefinitionError(
4219
+ this.#s.key,
4220
+ `mpp.settleBeforeHandler is incompatible with .settlement({ beforeSettle }) \u2014 MPP payment is already broadcast before the handler runs`
4220
4221
  );
4221
4222
  }
4222
4223
  }
4223
4224
  if (this.#s.billing === "upto") {
4224
4225
  const hasUpto = this.#s.deps.x402Accepts.some((accept) => accept.scheme === "upto");
4225
4226
  if (!hasUpto) {
4226
- throw new Error(
4227
- `route '${this.#s.key}': .upTo() requires an 'upto' accept on at least one configured network. Add { scheme: 'upto', network, asset } to RouterConfig.x402.accepts.`
4227
+ throw new RouteDefinitionError(
4228
+ this.#s.key,
4229
+ `.upTo() requires an 'upto' accept on at least one configured network. Add { scheme: 'upto', network, asset } to RouterConfig.x402.accepts.`
4228
4230
  );
4229
4231
  }
4230
4232
  }
@@ -4233,26 +4235,30 @@ var RouteBuilder = class _RouteBuilder {
4233
4235
  (accept) => (accept.scheme ?? "exact") !== "upto"
4234
4236
  );
4235
4237
  if (!hasExact) {
4236
- throw new Error(
4237
- `route '${this.#s.key}': .paid() needs a non-'upto' x402 accept \u2014 an 'upto'-only accept list cannot serve a fixed-price route. Add { scheme: 'exact', network } to RouterConfig.x402.accepts, or use .upTo() for handler-computed billing.`
4238
+ throw new RouteDefinitionError(
4239
+ this.#s.key,
4240
+ `.paid() needs a non-'upto' x402 accept \u2014 an 'upto'-only accept list cannot serve a fixed-price route. Add { scheme: 'exact', network } to RouterConfig.x402.accepts, or use .upTo() for handler-computed billing.`
4238
4241
  );
4239
4242
  }
4240
4243
  }
4241
4244
  if (this.#s.billing === "metered") {
4242
4245
  if (!this.#s.deps.mppSessionConfig) {
4243
- throw new Error(
4244
- `route '${this.#s.key}': .metered() requires MPP session mode. Set RouterConfig.mpp.session = {} and provide mpp.operatorKey.`
4246
+ throw new RouteDefinitionError(
4247
+ this.#s.key,
4248
+ `.metered() requires MPP session mode. Set RouterConfig.mpp.session = {} and provide mpp.operatorKey.`
4245
4249
  );
4246
4250
  }
4247
4251
  }
4248
4252
  if (streaming && this.#s.billing !== "metered") {
4249
- throw new Error(
4250
- `route '${this.#s.key}': .stream() requires .metered() \u2014 static/free/upto routes can't meter per-chunk billing.`
4253
+ throw new RouteDefinitionError(
4254
+ this.#s.key,
4255
+ `.stream() requires .metered() \u2014 static/free/upto routes can't meter per-chunk billing.`
4251
4256
  );
4252
4257
  }
4253
4258
  if (this.#s.description !== void 0 && this.#s.description.length > MAX_X402_DESCRIPTION_LENGTH && this.#s.pricing !== void 0 && this.#s.protocols.includes("x402")) {
4254
- throw new Error(
4255
- `route '${this.#s.key}': .description() is ${this.#s.description.length} chars; must be \u2264 ${MAX_X402_DESCRIPTION_LENGTH} chars \u2014 the CDP x402 facilitator rejects payments whose 402 challenge resource.description exceeds ~500 chars.`
4259
+ throw new RouteDefinitionError(
4260
+ this.#s.key,
4261
+ `.description() is ${this.#s.description.length} chars; must be \u2264 ${MAX_X402_DESCRIPTION_LENGTH} chars \u2014 the CDP x402 facilitator rejects payments whose 402 challenge resource.description exceeds ~500 chars.`
4256
4262
  );
4257
4263
  }
4258
4264
  validateExamples(
@@ -4320,14 +4326,15 @@ function normalizePaidArg(routeKey, arg, options) {
4320
4326
  if ("price" in arg && typeof arg.price === "string") {
4321
4327
  return { pricing: arg.price, resolvedOptions: arg, billing: "exact" };
4322
4328
  }
4323
- throw new Error(
4324
- `route '${routeKey}': .paid() requires one of: a price string, a (body) => string function, { price }, or { field, tiers }. For handler-computed billing use .upTo(); for per-tick billing use .metered().`
4329
+ throw new RouteDefinitionError(
4330
+ routeKey,
4331
+ `.paid() requires one of: a price string, a (body) => string function, { price }, or { field, tiers }. For handler-computed billing use .upTo(); for per-tick billing use .metered().`
4325
4332
  );
4326
4333
  }
4327
4334
  function normalizeUpToArg(routeKey, arg) {
4328
4335
  const options = typeof arg === "string" ? { maxPrice: arg } : arg;
4329
4336
  if (!options.maxPrice) {
4330
- throw new Error(`route '${routeKey}': .upTo() requires maxPrice`);
4337
+ throw new RouteDefinitionError(routeKey, `.upTo() requires maxPrice`);
4331
4338
  }
4332
4339
  return {
4333
4340
  pricing: options.maxPrice,
@@ -4339,10 +4346,10 @@ function normalizeUpToArg(routeKey, arg) {
4339
4346
  }
4340
4347
  function normalizeMeteredArg(routeKey, options) {
4341
4348
  if (!options.maxPrice) {
4342
- throw new Error(`route '${routeKey}': .metered() requires maxPrice`);
4349
+ throw new RouteDefinitionError(routeKey, `.metered() requires maxPrice`);
4343
4350
  }
4344
4351
  if (!options.tickCost) {
4345
- throw new Error(`route '${routeKey}': .metered() requires tickCost`);
4352
+ throw new RouteDefinitionError(routeKey, `.metered() requires tickCost`);
4346
4353
  }
4347
4354
  return {
4348
4355
  pricing: options.maxPrice,
@@ -4649,9 +4656,9 @@ function createNotFoundHandler(baseUrl) {
4649
4656
  code: "route_not_found",
4650
4657
  error: "Route not found. Rediscover this origin and retry with the current discovery document.",
4651
4658
  requestedUrl: request.url,
4659
+ // The deprecated /.well-known/x402 surface is intentionally not advertised.
4652
4660
  discovery: {
4653
4661
  openapi: `${normalizedBase}/openapi.json`,
4654
- wellKnown: `${normalizedBase}/.well-known/x402`,
4655
4662
  llmsTxt: `${normalizedBase}/llms.txt`
4656
4663
  }
4657
4664
  },
@@ -4684,6 +4691,7 @@ function formatRouterConfigIssues(issues) {
4684
4691
  }
4685
4692
 
4686
4693
  // src/config/schema.ts
4694
+ init_accepts();
4687
4695
  init_constants();
4688
4696
  import { z } from "zod";
4689
4697
 
@@ -4873,18 +4881,8 @@ function deriveBaseUrlEnv(env) {
4873
4881
  BASE_URL: vercelBaseUrl
4874
4882
  };
4875
4883
  }
4876
- function getConfiguredX402Accepts2(config) {
4877
- if (config.x402?.accepts?.length) return [...config.x402.accepts];
4878
- return [
4879
- {
4880
- scheme: "exact",
4881
- network: config.network ?? BASE_MAINNET_NETWORK,
4882
- payTo: config.payeeAddress
4883
- }
4884
- ];
4885
- }
4886
4884
  function validateX402Config(config, env) {
4887
- const accepts = getConfiguredX402Accepts2(config);
4885
+ const accepts = getConfiguredX402Accepts(config);
4888
4886
  const issues = [];
4889
4887
  const push = (code, message) => issues.push({ code, protocol: "x402", message });
4890
4888
  if (accepts.length === 0) {
@@ -5185,7 +5183,8 @@ function getMppxRequestContext(args) {
5185
5183
  recipient: mppConfig.recipient ?? payeeAddress,
5186
5184
  getClient,
5187
5185
  ...feePayerAccount ? { feePayer: feePayerAccount } : {},
5188
- ...resolvedStore ? { store: resolvedStore } : {}
5186
+ ...resolvedStore ? { store: resolvedStore } : {},
5187
+ ...mppConfig.feePayerPolicy ? { feePayerPolicy: mppConfig.feePayerPolicy } : {}
5189
5188
  }),
5190
5189
  ...sessionEnabled ? [
5191
5190
  tempo.session({
@@ -5223,7 +5222,7 @@ async function initMpp(config, resolvedBaseUrl, kvStore, configError) {
5223
5222
  try {
5224
5223
  const { Mppx, tempo } = await import("mppx/server");
5225
5224
  const { createClient, http } = await import("viem");
5226
- const { tempo: tempoChain } = await import("viem/chains");
5225
+ const { tempo: tempoChain } = await import("viem/tempo/chains");
5227
5226
  const { privateKeyToAccount: privateKeyToAccount2 } = await import("viem/accounts");
5228
5227
  const rpcUrl = config.mpp.rpcUrl ?? process.env.TEMPO_RPC_URL ?? DEFAULT_TEMPO_RPC_URL;
5229
5228
  const tempoClient = createClient({ chain: tempoChain, transport: http(rpcUrl) });
@@ -5241,7 +5240,9 @@ async function initMpp(config, resolvedBaseUrl, kvStore, configError) {
5241
5240
  getClient,
5242
5241
  ...operatorAccount ? { account: operatorAccount } : {},
5243
5242
  ...feePayerAccount ? { feePayer: feePayerAccount } : {},
5244
- ...resolvedStore ? { store: resolvedStore } : {}
5243
+ ...resolvedStore ? { store: resolvedStore } : {},
5244
+ ...mppConfig.feePayerPolicy ? { feePayerPolicy: mppConfig.feePayerPolicy } : {},
5245
+ ...mppConfig.session?.settlementSchedule ? { settlementSchedule: mppConfig.session.settlementSchedule } : {}
5245
5246
  };
5246
5247
  const mppxArgs = {
5247
5248
  Mppx,
@@ -5257,6 +5258,7 @@ async function initMpp(config, resolvedBaseUrl, kvStore, configError) {
5257
5258
  };
5258
5259
  const primary = getMppxRequestContext(mppxArgs);
5259
5260
  const streaming = getMppxStreamingContext(mppxArgs);
5261
+ subscribePaymentFailedAlerts(config.plugin, [primary, streaming]);
5260
5262
  const mppx = {
5261
5263
  charge: primary.charge,
5262
5264
  ...primary.session ? { sessionRequest: primary.session } : {},
@@ -5267,6 +5269,41 @@ async function initMpp(config, resolvedBaseUrl, kvStore, configError) {
5267
5269
  return { initError: err instanceof Error ? err.message : String(err) };
5268
5270
  }
5269
5271
  }
5272
+ function subscribePaymentFailedAlerts(plugin, instances) {
5273
+ if (!plugin?.onAlert) return;
5274
+ const ctx = {
5275
+ requestId: "mppx",
5276
+ route: "mpp",
5277
+ walletAddress: null,
5278
+ clientId: null,
5279
+ sessionId: null,
5280
+ verifiedWallet: null,
5281
+ setVerifiedWallet() {
5282
+ }
5283
+ };
5284
+ for (const instance of instances) {
5285
+ const events = instance;
5286
+ if (typeof events?.onPaymentFailed !== "function") continue;
5287
+ events.onPaymentFailed((payload) => {
5288
+ try {
5289
+ const error = payload?.error ?? {};
5290
+ firePluginHook(plugin, "onAlert", ctx, {
5291
+ level: (error.status ?? 402) >= 500 ? "error" : "warn",
5292
+ message: `MPP payment failed: ${error.message ?? error.name ?? "unknown error"}`,
5293
+ route: "mpp",
5294
+ meta: {
5295
+ ...payload?.method ? { method: `${payload.method.name}/${payload.method.intent}` } : {},
5296
+ ...error.type ? { errorType: error.type } : {},
5297
+ ...error.status !== void 0 ? { status: error.status } : {},
5298
+ ...error.hint ? { hint: error.hint } : {},
5299
+ ...payload?.credential?.source ? { payer: payload.credential.source } : {}
5300
+ }
5301
+ });
5302
+ } catch {
5303
+ }
5304
+ });
5305
+ }
5306
+ }
5270
5307
 
5271
5308
  // src/index.ts
5272
5309
  init_constants();
@@ -5409,6 +5446,7 @@ export {
5409
5446
  DEFAULT_SOLANA_FACILITATOR_URL,
5410
5447
  DEFAULT_TEMPO_RPC_URL,
5411
5448
  HttpError,
5449
+ RouteDefinitionError,
5412
5450
  RouterConfigError,
5413
5451
  SOLANA_MAINNET_NETWORK,
5414
5452
  TEMPO_USDC_ADDRESS,