@agentcash/router 1.10.7 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -39,6 +39,8 @@ src/
39
39
 
40
40
  `auth check -> body parse -> validate -> 402 challenge -> payment verify -> handler -> settle -> finalize`
41
41
 
42
+ Paid `.paid()` routes may set `mpp: { settleBeforeHandler: true }` (or chain `.mpp({ settleBeforeHandler: true })` on auto-priced routes) to broadcast MPP transaction (pull) credentials at verify instead of after the handler. x402 on the same route is unaffected. MPP hash (push) credentials already settle at verify. Use `.settlement({ onSettledHandlerError })` when eager MPP settle can charge before an upstream failure.
43
+
42
44
  ## Naming
43
45
 
44
46
  Constructor-style functions use `build<Noun>` — one verb, one domain noun. Name them after the domain concept, never after an HTTP status code or transport detail (the function that builds a payment challenge is `buildChallengeResponse`, not `build402`). The challenge family shares the `Challenge` backbone: `buildChallengeResponse`, `buildChallengeExtensions`, `buildSiwxChallenge`, `buildSessionChallenge`, `buildX402Challenge`.
package/dist/index.cjs CHANGED
@@ -1784,7 +1784,8 @@ var mppStrategy = {
1784
1784
  return verifySessionMode(args, info);
1785
1785
  }
1786
1786
  if (info.sessionAction) return { ok: false, kind: "invalid" };
1787
- if (info.payloadType === "transaction" && args.deps.tempoClient) {
1787
+ const deferTransactionSettlement = info.payloadType === "transaction" && args.deps.tempoClient && !args.routeEntry.mppInfo?.settleBeforeHandler;
1788
+ if (deferTransactionSettlement) {
1788
1789
  return verifyTxMode(args, info);
1789
1790
  }
1790
1791
  return verifyHashMode(args, info);
@@ -3856,7 +3857,9 @@ var RouteBuilder = class _RouteBuilder {
3856
3857
  if (maxPrice) next.#s.maxPrice = maxPrice;
3857
3858
  if (resolvedOptions.minPrice) next.#s.minPrice = resolvedOptions.minPrice;
3858
3859
  if (resolvedOptions.payTo) next.#s.payTo = resolvedOptions.payTo;
3859
- if (resolvedOptions.mpp) next.#s.mppInfo = resolvedOptions.mpp;
3860
+ if (resolvedOptions.mpp) {
3861
+ next.#s.mppInfo = { ...next.#s.mppInfo, ...resolvedOptions.mpp };
3862
+ }
3860
3863
  if (resolvedOptions.checkout || resolvedOptions.checkoutSession) next.#s.hasCheckout = true;
3861
3864
  if (resolvedOptions.checkoutSession) next.#s.checkoutSession = resolvedOptions.checkoutSession;
3862
3865
  next.#s.billing = billing;
@@ -4151,6 +4154,24 @@ var RouteBuilder = class _RouteBuilder {
4151
4154
  next.#s.validateFn = fn;
4152
4155
  return next;
4153
4156
  }
4157
+ /**
4158
+ * Override MPP protocol metadata and per-route MPP settlement options.
4159
+ * Merges with any `mpp` options passed to `.paid()`. Use on auto-priced routes
4160
+ * (from `RouterConfig.prices`) when you cannot call `.paid()` again.
4161
+ *
4162
+ * @example
4163
+ * ```ts
4164
+ * router.route('exa/answer')
4165
+ * .mpp({ settleBeforeHandler: true })
4166
+ * .body(schema)
4167
+ * .handler(handler);
4168
+ * ```
4169
+ */
4170
+ mpp(info) {
4171
+ const next = this.fork();
4172
+ next.#s.mppInfo = { ...this.#s.mppInfo, ...info };
4173
+ return next;
4174
+ }
4154
4175
  /**
4155
4176
  * Hook into the settlement lifecycle. `beforeSettle` runs after the handler
4156
4177
  * succeeds but before on-chain settlement and can cancel the charge;
@@ -4166,7 +4187,7 @@ var RouteBuilder = class _RouteBuilder {
4166
4187
  */
4167
4188
  settlement(lifecycle) {
4168
4189
  const next = this.fork();
4169
- next.#s.settlement = lifecycle;
4190
+ next.#s.settlement = { ...this.#s.settlement, ...lifecycle };
4170
4191
  return next;
4171
4192
  }
4172
4193
  /**
@@ -4222,6 +4243,21 @@ var RouteBuilder = class _RouteBuilder {
4222
4243
  if (this.#s.settlement && !this.#s.pricing) {
4223
4244
  throw new Error(`route '${this.#s.key}': .settlement() requires a paid route`);
4224
4245
  }
4246
+ if (this.#s.mppInfo?.settleBeforeHandler) {
4247
+ if (!this.#s.pricing) {
4248
+ throw new Error(`route '${this.#s.key}': mpp.settleBeforeHandler requires a paid route`);
4249
+ }
4250
+ if (this.#s.billing !== "exact") {
4251
+ throw new Error(
4252
+ `route '${this.#s.key}': mpp.settleBeforeHandler is only supported on .paid() routes`
4253
+ );
4254
+ }
4255
+ if (this.#s.settlement?.beforeSettle) {
4256
+ throw new Error(
4257
+ `route '${this.#s.key}': mpp.settleBeforeHandler is incompatible with .settlement({ beforeSettle }) \u2014 MPP payment is already broadcast before the handler runs`
4258
+ );
4259
+ }
4260
+ }
4225
4261
  if (this.#s.billing === "upto") {
4226
4262
  const hasUpto = this.#s.deps.x402Accepts.some((accept) => accept.scheme === "upto");
4227
4263
  if (!hasUpto) {
package/dist/index.d.cts CHANGED
@@ -221,6 +221,7 @@ interface MppProtocolInfo {
221
221
  method?: string;
222
222
  intent?: string;
223
223
  currency?: string;
224
+ settleBeforeHandler?: boolean;
224
225
  }
225
226
  type CheckoutSessionResponse = Record<string, unknown>;
226
227
  interface CheckoutSessionContext<TBody = unknown> {
@@ -409,6 +410,7 @@ interface DiscoveryConfig {
409
410
  contact?: {
410
411
  name?: string;
411
412
  url?: string;
413
+ email?: string;
412
414
  };
413
415
  ownershipProofs?: string[];
414
416
  methodHints?: 'off' | 'non-default' | 'always';
@@ -582,8 +584,9 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
582
584
  * - `{ field, tiers, default? }` — pick a tier from `body[field]`.
583
585
  *
584
586
  * Common knobs (`protocols`, `maxPrice`, `minPrice`, `payTo`, `mpp`, `checkout`,
585
- * `checkoutSession`) live alongside the pricing shape. For handler-computed billing use `.upTo()`;
586
- * for per-tick billing use `.metered()`.
587
+ * `checkoutSession`) live alongside the pricing shape. Set `mpp.settleBeforeHandler`
588
+ * on slow upstream routes so MPP transaction (pull) credentials broadcast at verify.
589
+ * For auto-priced routes from `RouterConfig.prices`, chain `.mpp({ settleBeforeHandler: true })`.
587
590
  *
588
591
  * @example
589
592
  * ```ts
@@ -779,6 +782,20 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
779
782
  * ```
780
783
  */
781
784
  validate(fn: (body: TBody) => void | Promise<void>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, Bill>;
785
+ /**
786
+ * Override MPP protocol metadata and per-route MPP settlement options.
787
+ * Merges with any `mpp` options passed to `.paid()`. Use on auto-priced routes
788
+ * (from `RouterConfig.prices`) when you cannot call `.paid()` again.
789
+ *
790
+ * @example
791
+ * ```ts
792
+ * router.route('exa/answer')
793
+ * .mpp({ settleBeforeHandler: true })
794
+ * .body(schema)
795
+ * .handler(handler);
796
+ * ```
797
+ */
798
+ mpp(info: MppProtocolInfo): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, Bill>;
782
799
  /**
783
800
  * Hook into the settlement lifecycle. `beforeSettle` runs after the handler
784
801
  * succeeds but before on-chain settlement and can cancel the charge;
@@ -953,4 +970,4 @@ declare function createRouter<const P extends Record<string, string> = Record<ne
953
970
  */
954
971
  declare function createRouterFromEnv<const P extends Record<string, string> = Record<never, string>>(options: CreateRouterFromEnvOptions<P>): ServiceRouter<Extract<keyof P, string>>;
955
972
 
956
- export { BASE_MAINNET_NETWORK, BASE_USDC_ADDRESS, BASE_USDC_DECIMALS, type CheckoutSessionContext, type CheckoutSessionFn, type CheckoutSessionResponse, type CreateRouterFromEnvOptions, DEFAULT_SOLANA_FACILITATOR_URL, DEFAULT_TEMPO_RPC_URL, type DiscoveryConfig, type HandlerContext, HttpError, type KvStore, type PaidOptions, type ProtocolType, type RouterConfig, RouterConfigError, type RouterConfigIssue, type RouterConfigIssueCode, type RouterConfigIssueSeverity, type RouterPlugin, SOLANA_MAINNET_NETWORK, type ServiceRouter, type SettlementErrorContext, type SettlementLifecycleContext, type SettlementSettledContext, TEMPO_USDC_ADDRESS, TEMPO_USDC_DECIMALS, type X402FacilitatorsConfig, ZERO_EVM_ADDRESS, createRouter, createRouterFromEnv, routerConfigFromEnv };
973
+ export { BASE_MAINNET_NETWORK, BASE_USDC_ADDRESS, BASE_USDC_DECIMALS, type CheckoutSessionContext, type CheckoutSessionFn, type CheckoutSessionResponse, type CreateRouterFromEnvOptions, DEFAULT_SOLANA_FACILITATOR_URL, DEFAULT_TEMPO_RPC_URL, type DiscoveryConfig, type HandlerContext, HttpError, type KvStore, type MppProtocolInfo, type PaidOptions, type ProtocolType, type RouterConfig, RouterConfigError, type RouterConfigIssue, type RouterConfigIssueCode, type RouterConfigIssueSeverity, type RouterPlugin, SOLANA_MAINNET_NETWORK, type ServiceRouter, type SettlementErrorContext, type SettlementLifecycleContext, type SettlementSettledContext, TEMPO_USDC_ADDRESS, TEMPO_USDC_DECIMALS, type X402FacilitatorsConfig, ZERO_EVM_ADDRESS, createRouter, createRouterFromEnv, routerConfigFromEnv };
package/dist/index.d.ts CHANGED
@@ -221,6 +221,7 @@ interface MppProtocolInfo {
221
221
  method?: string;
222
222
  intent?: string;
223
223
  currency?: string;
224
+ settleBeforeHandler?: boolean;
224
225
  }
225
226
  type CheckoutSessionResponse = Record<string, unknown>;
226
227
  interface CheckoutSessionContext<TBody = unknown> {
@@ -409,6 +410,7 @@ interface DiscoveryConfig {
409
410
  contact?: {
410
411
  name?: string;
411
412
  url?: string;
413
+ email?: string;
412
414
  };
413
415
  ownershipProofs?: string[];
414
416
  methodHints?: 'off' | 'non-default' | 'always';
@@ -582,8 +584,9 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
582
584
  * - `{ field, tiers, default? }` — pick a tier from `body[field]`.
583
585
  *
584
586
  * Common knobs (`protocols`, `maxPrice`, `minPrice`, `payTo`, `mpp`, `checkout`,
585
- * `checkoutSession`) live alongside the pricing shape. For handler-computed billing use `.upTo()`;
586
- * for per-tick billing use `.metered()`.
587
+ * `checkoutSession`) live alongside the pricing shape. Set `mpp.settleBeforeHandler`
588
+ * on slow upstream routes so MPP transaction (pull) credentials broadcast at verify.
589
+ * For auto-priced routes from `RouterConfig.prices`, chain `.mpp({ settleBeforeHandler: true })`.
587
590
  *
588
591
  * @example
589
592
  * ```ts
@@ -779,6 +782,20 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
779
782
  * ```
780
783
  */
781
784
  validate(fn: (body: TBody) => void | Promise<void>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, Bill>;
785
+ /**
786
+ * Override MPP protocol metadata and per-route MPP settlement options.
787
+ * Merges with any `mpp` options passed to `.paid()`. Use on auto-priced routes
788
+ * (from `RouterConfig.prices`) when you cannot call `.paid()` again.
789
+ *
790
+ * @example
791
+ * ```ts
792
+ * router.route('exa/answer')
793
+ * .mpp({ settleBeforeHandler: true })
794
+ * .body(schema)
795
+ * .handler(handler);
796
+ * ```
797
+ */
798
+ mpp(info: MppProtocolInfo): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, Bill>;
782
799
  /**
783
800
  * Hook into the settlement lifecycle. `beforeSettle` runs after the handler
784
801
  * succeeds but before on-chain settlement and can cancel the charge;
@@ -953,4 +970,4 @@ declare function createRouter<const P extends Record<string, string> = Record<ne
953
970
  */
954
971
  declare function createRouterFromEnv<const P extends Record<string, string> = Record<never, string>>(options: CreateRouterFromEnvOptions<P>): ServiceRouter<Extract<keyof P, string>>;
955
972
 
956
- export { BASE_MAINNET_NETWORK, BASE_USDC_ADDRESS, BASE_USDC_DECIMALS, type CheckoutSessionContext, type CheckoutSessionFn, type CheckoutSessionResponse, type CreateRouterFromEnvOptions, DEFAULT_SOLANA_FACILITATOR_URL, DEFAULT_TEMPO_RPC_URL, type DiscoveryConfig, type HandlerContext, HttpError, type KvStore, type PaidOptions, type ProtocolType, type RouterConfig, RouterConfigError, type RouterConfigIssue, type RouterConfigIssueCode, type RouterConfigIssueSeverity, type RouterPlugin, SOLANA_MAINNET_NETWORK, type ServiceRouter, type SettlementErrorContext, type SettlementLifecycleContext, type SettlementSettledContext, TEMPO_USDC_ADDRESS, TEMPO_USDC_DECIMALS, type X402FacilitatorsConfig, ZERO_EVM_ADDRESS, createRouter, createRouterFromEnv, routerConfigFromEnv };
973
+ export { BASE_MAINNET_NETWORK, BASE_USDC_ADDRESS, BASE_USDC_DECIMALS, type CheckoutSessionContext, type CheckoutSessionFn, type CheckoutSessionResponse, type CreateRouterFromEnvOptions, DEFAULT_SOLANA_FACILITATOR_URL, DEFAULT_TEMPO_RPC_URL, type DiscoveryConfig, type HandlerContext, HttpError, type KvStore, type MppProtocolInfo, type PaidOptions, type ProtocolType, type RouterConfig, RouterConfigError, type RouterConfigIssue, type RouterConfigIssueCode, type RouterConfigIssueSeverity, type RouterPlugin, SOLANA_MAINNET_NETWORK, type ServiceRouter, type SettlementErrorContext, type SettlementLifecycleContext, type SettlementSettledContext, TEMPO_USDC_ADDRESS, TEMPO_USDC_DECIMALS, type X402FacilitatorsConfig, ZERO_EVM_ADDRESS, createRouter, createRouterFromEnv, routerConfigFromEnv };
package/dist/index.js CHANGED
@@ -1746,7 +1746,8 @@ var mppStrategy = {
1746
1746
  return verifySessionMode(args, info);
1747
1747
  }
1748
1748
  if (info.sessionAction) return { ok: false, kind: "invalid" };
1749
- if (info.payloadType === "transaction" && args.deps.tempoClient) {
1749
+ const deferTransactionSettlement = info.payloadType === "transaction" && args.deps.tempoClient && !args.routeEntry.mppInfo?.settleBeforeHandler;
1750
+ if (deferTransactionSettlement) {
1750
1751
  return verifyTxMode(args, info);
1751
1752
  }
1752
1753
  return verifyHashMode(args, info);
@@ -3818,7 +3819,9 @@ var RouteBuilder = class _RouteBuilder {
3818
3819
  if (maxPrice) next.#s.maxPrice = maxPrice;
3819
3820
  if (resolvedOptions.minPrice) next.#s.minPrice = resolvedOptions.minPrice;
3820
3821
  if (resolvedOptions.payTo) next.#s.payTo = resolvedOptions.payTo;
3821
- if (resolvedOptions.mpp) next.#s.mppInfo = resolvedOptions.mpp;
3822
+ if (resolvedOptions.mpp) {
3823
+ next.#s.mppInfo = { ...next.#s.mppInfo, ...resolvedOptions.mpp };
3824
+ }
3822
3825
  if (resolvedOptions.checkout || resolvedOptions.checkoutSession) next.#s.hasCheckout = true;
3823
3826
  if (resolvedOptions.checkoutSession) next.#s.checkoutSession = resolvedOptions.checkoutSession;
3824
3827
  next.#s.billing = billing;
@@ -4113,6 +4116,24 @@ var RouteBuilder = class _RouteBuilder {
4113
4116
  next.#s.validateFn = fn;
4114
4117
  return next;
4115
4118
  }
4119
+ /**
4120
+ * Override MPP protocol metadata and per-route MPP settlement options.
4121
+ * Merges with any `mpp` options passed to `.paid()`. Use on auto-priced routes
4122
+ * (from `RouterConfig.prices`) when you cannot call `.paid()` again.
4123
+ *
4124
+ * @example
4125
+ * ```ts
4126
+ * router.route('exa/answer')
4127
+ * .mpp({ settleBeforeHandler: true })
4128
+ * .body(schema)
4129
+ * .handler(handler);
4130
+ * ```
4131
+ */
4132
+ mpp(info) {
4133
+ const next = this.fork();
4134
+ next.#s.mppInfo = { ...this.#s.mppInfo, ...info };
4135
+ return next;
4136
+ }
4116
4137
  /**
4117
4138
  * Hook into the settlement lifecycle. `beforeSettle` runs after the handler
4118
4139
  * succeeds but before on-chain settlement and can cancel the charge;
@@ -4128,7 +4149,7 @@ var RouteBuilder = class _RouteBuilder {
4128
4149
  */
4129
4150
  settlement(lifecycle) {
4130
4151
  const next = this.fork();
4131
- next.#s.settlement = lifecycle;
4152
+ next.#s.settlement = { ...this.#s.settlement, ...lifecycle };
4132
4153
  return next;
4133
4154
  }
4134
4155
  /**
@@ -4184,6 +4205,21 @@ var RouteBuilder = class _RouteBuilder {
4184
4205
  if (this.#s.settlement && !this.#s.pricing) {
4185
4206
  throw new Error(`route '${this.#s.key}': .settlement() requires a paid route`);
4186
4207
  }
4208
+ if (this.#s.mppInfo?.settleBeforeHandler) {
4209
+ if (!this.#s.pricing) {
4210
+ throw new Error(`route '${this.#s.key}': mpp.settleBeforeHandler requires a paid route`);
4211
+ }
4212
+ if (this.#s.billing !== "exact") {
4213
+ throw new Error(
4214
+ `route '${this.#s.key}': mpp.settleBeforeHandler is only supported on .paid() routes`
4215
+ );
4216
+ }
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`
4220
+ );
4221
+ }
4222
+ }
4187
4223
  if (this.#s.billing === "upto") {
4188
4224
  const hasUpto = this.#s.deps.x402Accepts.some((accept) => accept.scheme === "upto");
4189
4225
  if (!hasUpto) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/router",
3
- "version": "1.10.7",
3
+ "version": "1.12.0",
4
4
  "description": "Unified route builder for Next.js App Router APIs with x402, MPP, SIWX, and API key auth",
5
5
  "type": "module",
6
6
  "exports": {