@agentcash/router 1.6.0 → 1.7.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.cjs CHANGED
@@ -31,14 +31,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
33
  // src/constants.ts
34
- var BASE_NETWORK, SOLANA_MAINNET_NETWORK, TEMPO_USDC_CURRENCY, ZERO_EVM_ADDRESS;
34
+ var BASE_MAINNET_NETWORK, SOLANA_MAINNET_NETWORK, TEMPO_USDC_ADDRESS, TEMPO_USDC_DECIMALS, BASE_USDC_ADDRESS, BASE_USDC_DECIMALS, ZERO_EVM_ADDRESS, DEFAULT_SOLANA_FACILITATOR_URL;
35
35
  var init_constants = __esm({
36
36
  "src/constants.ts"() {
37
37
  "use strict";
38
- BASE_NETWORK = "eip155:8453";
38
+ BASE_MAINNET_NETWORK = "eip155:8453";
39
39
  SOLANA_MAINNET_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
40
- TEMPO_USDC_CURRENCY = "0x20c000000000000000000000b9537d11c60e8b50";
40
+ TEMPO_USDC_ADDRESS = "0x20c000000000000000000000b9537d11c60e8b50";
41
+ TEMPO_USDC_DECIMALS = 6;
42
+ BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
43
+ BASE_USDC_DECIMALS = 6;
41
44
  ZERO_EVM_ADDRESS = "0x0000000000000000000000000000000000000000";
45
+ DEFAULT_SOLANA_FACILITATOR_URL = "https://facilitator.corbits.dev";
42
46
  }
43
47
  });
44
48
 
@@ -55,7 +59,7 @@ function getConfiguredX402Accepts(config) {
55
59
  return [
56
60
  {
57
61
  scheme: "exact",
58
- network: config.network ?? BASE_NETWORK,
62
+ network: config.network ?? BASE_MAINNET_NETWORK,
59
63
  payTo: config.payeeAddress
60
64
  }
61
65
  ];
@@ -235,7 +239,10 @@ async function getAcceptsHeadersForFacilitator(facilitator) {
235
239
  return {};
236
240
  }
237
241
  function resolveX402FacilitatorTarget(config, network, defaultEvmFacilitator) {
238
- return (isSolanaNetwork(network) ? config.x402?.facilitators?.solana : void 0) ?? (isEvmNetwork(network) ? config.x402?.facilitators?.evm : void 0) ?? (isSolanaNetwork(network) ? DEFAULT_SOLANA_FACILITATOR_URL : defaultEvmFacilitator);
242
+ if (isSolanaNetwork(network)) {
243
+ return config.x402?.facilitators?.solana ?? DEFAULT_SOLANA_FACILITATOR_URL;
244
+ }
245
+ return defaultEvmFacilitator;
239
246
  }
240
247
  function normalizeFacilitatorTarget(target) {
241
248
  return typeof target === "string" ? { url: target } : target;
@@ -248,19 +255,18 @@ function getNetworkFamily(network) {
248
255
  function sameFacilitatorConfig(a, b) {
249
256
  return a.url === b.url && a.createAuthHeaders === b.createAuthHeaders && a.createAcceptsHeaders === b.createAcceptsHeaders;
250
257
  }
251
- var DEFAULT_SOLANA_FACILITATOR_URL;
252
258
  var init_facilitators = __esm({
253
259
  "src/protocols/x402/facilitators.ts"() {
254
260
  "use strict";
255
261
  init_evm();
256
262
  init_solana();
257
- DEFAULT_SOLANA_FACILITATOR_URL = "https://facilitator.corbits.dev";
263
+ init_constants();
258
264
  }
259
265
  });
260
266
 
261
- // src/server.ts
262
- var server_exports = {};
263
- __export(server_exports, {
267
+ // src/init/x402-server.ts
268
+ var x402_server_exports = {};
269
+ __export(x402_server_exports, {
264
270
  createX402Server: () => createX402Server
265
271
  });
266
272
  async function createX402Server(config) {
@@ -337,8 +343,8 @@ function buildSupportedKinds(group) {
337
343
  return [exactKind, uptoKind];
338
344
  });
339
345
  }
340
- var init_server = __esm({
341
- "src/server.ts"() {
346
+ var init_x402_server = __esm({
347
+ "src/init/x402-server.ts"() {
342
348
  "use strict";
343
349
  init_evm();
344
350
  init_solana();
@@ -350,30 +356,19 @@ var init_server = __esm({
350
356
  // src/index.ts
351
357
  var index_exports = {};
352
358
  __export(index_exports, {
353
- BASE_NETWORK: () => BASE_NETWORK,
359
+ BASE_MAINNET_NETWORK: () => BASE_MAINNET_NETWORK,
360
+ BASE_USDC_ADDRESS: () => BASE_USDC_ADDRESS,
361
+ BASE_USDC_DECIMALS: () => BASE_USDC_DECIMALS,
362
+ DEFAULT_SOLANA_FACILITATOR_URL: () => DEFAULT_SOLANA_FACILITATOR_URL,
354
363
  HttpError: () => HttpError,
355
- MemoryEntitlementStore: () => MemoryEntitlementStore,
356
- MemoryNonceStore: () => MemoryNonceStore,
357
- RouteBuilder: () => RouteBuilder,
358
- RouteRegistry: () => RouteRegistry,
359
364
  RouterConfigError: () => RouterConfigError,
360
- SIWX_CHALLENGE_EXPIRY_MS: () => SIWX_CHALLENGE_EXPIRY_MS,
361
- SIWX_ERROR_MESSAGES: () => SIWX_ERROR_MESSAGES,
362
365
  SOLANA_MAINNET_NETWORK: () => SOLANA_MAINNET_NETWORK,
363
- TEMPO_USDC_CURRENCY: () => TEMPO_USDC_CURRENCY,
366
+ TEMPO_USDC_ADDRESS: () => TEMPO_USDC_ADDRESS,
367
+ TEMPO_USDC_DECIMALS: () => TEMPO_USDC_DECIMALS,
364
368
  ZERO_EVM_ADDRESS: () => ZERO_EVM_ADDRESS,
365
- consolePlugin: () => consolePlugin,
366
- createKvEntitlementStore: () => createKvEntitlementStore,
367
- createKvMppStore: () => createKvMppStore,
368
- createKvNonceStore: () => createKvNonceStore,
369
369
  createRouter: () => createRouter,
370
- formatRouterConfigIssues: () => formatRouterConfigIssues,
371
- getRouterConfigIssues: () => getRouterConfigIssues,
372
- mppFromEnv: () => mppFromEnv,
373
- paidOptionsForProtocols: () => paidOptionsForProtocols,
374
- validateRouterConfig: () => validateRouterConfig,
375
- withPrefix: () => withPrefix,
376
- x402AcceptsFromEnv: () => x402AcceptsFromEnv
370
+ createRouterFromEnv: () => createRouterFromEnv,
371
+ routerConfigFromEnv: () => routerConfigFromEnv
377
372
  });
378
373
  module.exports = __toCommonJS(index_exports);
379
374
 
@@ -441,7 +436,7 @@ var AUTH_SCHEME = {
441
436
  MPP_PAYMENT: "Payment "
442
437
  };
443
438
 
444
- // src/plugin.ts
439
+ // src/plugin/index.ts
445
440
  function createDefaultContext(meta) {
446
441
  const ctx = {
447
442
  requestId: meta.requestId,
@@ -477,46 +472,8 @@ function firePluginHook(plugin, method, ...args) {
477
472
  return void 0;
478
473
  }
479
474
  }
480
- function consolePlugin() {
481
- return {
482
- onRequest(meta) {
483
- const ctx = createDefaultContext(meta);
484
- return ctx;
485
- },
486
- onAuthVerified(_ctx, auth) {
487
- const wallet = auth.wallet ? ` wallet=${auth.wallet}` : "";
488
- console.log(`[router] AUTH ${auth.authMode} ${auth.route}${wallet}`);
489
- },
490
- onPaymentVerified(_ctx, payment) {
491
- console.log(`[router] VERIFIED ${payment.protocol} ${payment.payer} ${payment.amount}`);
492
- },
493
- onPaymentSettled(_ctx, settlement) {
494
- console.log(`[router] SETTLED ${settlement.protocol} tx=${settlement.transaction}`);
495
- },
496
- onResponse(ctx, response) {
497
- const wallet = ctx.verifiedWallet ? ` wallet=${ctx.verifiedWallet}` : "";
498
- console.log(
499
- `[router] ${ctx.route} \u2192 ${response.statusCode} (${response.duration}ms)${wallet}`
500
- );
501
- },
502
- onError(_ctx, error) {
503
- console.error(`[router] ERROR ${error.status}: ${error.message}`);
504
- },
505
- onAlert(_ctx, alert) {
506
- const logFn = alert.level === "critical" || alert.level === "error" ? console.error : alert.level === "warn" ? console.warn : console.log;
507
- logFn(
508
- `[router] ${alert.level.toUpperCase()} ${alert.route}: ${alert.message}`,
509
- alert.meta ?? ""
510
- );
511
- },
512
- onProviderQuota(_ctx, event) {
513
- const logFn = event.level === "critical" ? console.error : event.level === "warn" ? console.warn : console.log;
514
- logFn(`[router] QUOTA ${event.level.toUpperCase()} ${event.provider}: ${event.message}`);
515
- }
516
- };
517
- }
518
475
 
519
- // src/alert.ts
476
+ // src/plugin/reporter.ts
520
477
  function createReporter(plugin, pluginCtx, route) {
521
478
  return (level, message, meta) => {
522
479
  firePluginHook(plugin, "onAlert", pluginCtx, {
@@ -528,7 +485,7 @@ function createReporter(plugin, pluginCtx, route) {
528
485
  };
529
486
  }
530
487
 
531
- // src/pipeline/context/preflight.ts
488
+ // src/pipeline/steps/preflight.ts
532
489
  function preflight(routeEntry, handler, deps, request) {
533
490
  const meta = buildMeta(request, routeEntry);
534
491
  const pluginCtx = firePluginHook(deps.plugin, "onRequest", meta) ?? createDefaultContext(meta);
@@ -558,10 +515,10 @@ function buildMeta(request, routeEntry) {
558
515
  };
559
516
  }
560
517
 
561
- // src/pipeline/context/parse-body.ts
518
+ // src/pipeline/steps/parse-body.ts
562
519
  var import_server = require("next/server");
563
520
 
564
- // src/body.ts
521
+ // src/pipeline/body.ts
565
522
  async function bufferBody(request) {
566
523
  const text = await request.text();
567
524
  if (!text) return void 0;
@@ -585,7 +542,19 @@ function validateBody(parsed, schema) {
585
542
  };
586
543
  }
587
544
 
588
- // src/pipeline/context/fire-plugin-response.ts
545
+ // src/plugin/events.ts
546
+ function fireAuthVerified(ctx, event) {
547
+ firePluginHook(ctx.deps.plugin, "onAuthVerified", ctx.pluginCtx, {
548
+ ...event,
549
+ route: ctx.routeEntry.key
550
+ });
551
+ }
552
+ function firePaymentVerified(ctx, event) {
553
+ firePluginHook(ctx.deps.plugin, "onPaymentVerified", ctx.pluginCtx, event);
554
+ }
555
+ function firePaymentSettled(ctx, event) {
556
+ firePluginHook(ctx.deps.plugin, "onPaymentSettled", ctx.pluginCtx, event);
557
+ }
589
558
  function firePluginResponse(ctx, response, requestBody, responseBody) {
590
559
  firePluginHook(ctx.deps.plugin, "onResponse", ctx.pluginCtx, {
591
560
  statusCode: response.status,
@@ -604,8 +573,37 @@ function firePluginResponse(ctx, response, requestBody, responseBody) {
604
573
  });
605
574
  }
606
575
  }
576
+ function fireProviderQuota(ctx, response, handlerResult) {
577
+ const { providerName, providerConfig } = ctx.routeEntry;
578
+ if (!providerName || !providerConfig?.extractQuota) return;
579
+ if (response.status >= 400) return;
580
+ try {
581
+ const quota = providerConfig.extractQuota(handlerResult, response.headers);
582
+ if (!quota) return;
583
+ const level = computeQuotaLevel(quota.remaining, providerConfig.warn, providerConfig.critical);
584
+ const overage = providerConfig.overage ?? "same-rate";
585
+ const event = {
586
+ provider: providerName,
587
+ route: ctx.routeEntry.key,
588
+ remaining: quota.remaining,
589
+ limit: quota.limit,
590
+ spend: quota.spend,
591
+ level,
592
+ overage,
593
+ message: quota.remaining !== null ? `${providerName}: ${quota.remaining}${quota.limit ? `/${quota.limit}` : ""} remaining` : `${providerName}: quota info unavailable`
594
+ };
595
+ firePluginHook(ctx.deps.plugin, "onProviderQuota", ctx.pluginCtx, event);
596
+ } catch {
597
+ }
598
+ }
599
+ function computeQuotaLevel(remaining, warn, critical) {
600
+ if (remaining === null) return "healthy";
601
+ if (critical !== void 0 && remaining <= critical) return "critical";
602
+ if (warn !== void 0 && remaining <= warn) return "warn";
603
+ return "healthy";
604
+ }
607
605
 
608
- // src/pipeline/context/parse-body.ts
606
+ // src/pipeline/steps/parse-body.ts
609
607
  async function parseBody(ctx, request = ctx.request) {
610
608
  if (!ctx.routeEntry.bodySchema) return { ok: true, data: void 0 };
611
609
  const raw = await bufferBody(request);
@@ -619,15 +617,7 @@ async function parseBody(ctx, request = ctx.request) {
619
617
  return { ok: false, response };
620
618
  }
621
619
 
622
- // src/pipeline/context/parse-query.ts
623
- function parseQuery(request, routeEntry) {
624
- if (!routeEntry.querySchema) return void 0;
625
- const params = Object.fromEntries(request.nextUrl.searchParams.entries());
626
- const result = routeEntry.querySchema.safeParse(params);
627
- return result.success ? result.data : params;
628
- }
629
-
630
- // src/pipeline/context/errors.ts
620
+ // src/pipeline/steps/errors.ts
631
621
  function errorStatus(error, fallback) {
632
622
  const status = error?.status;
633
623
  return typeof status === "number" ? status : fallback;
@@ -640,7 +630,7 @@ function handlerFailureError(response) {
640
630
  return Object.assign(new Error(message), { status: response.status });
641
631
  }
642
632
 
643
- // src/pipeline/context/fail.ts
633
+ // src/pipeline/steps/fail.ts
644
634
  var import_server2 = require("next/server");
645
635
  function fail(ctx, status, message, requestBody) {
646
636
  const response = import_server2.NextResponse.json({ success: false, error: message }, { status });
@@ -648,7 +638,7 @@ function fail(ctx, status, message, requestBody) {
648
638
  return response;
649
639
  }
650
640
 
651
- // src/pipeline/context/run-validate.ts
641
+ // src/pipeline/steps/run-validate.ts
652
642
  async function runValidate(ctx, body) {
653
643
  if (!ctx.routeEntry.validateFn) return null;
654
644
  try {
@@ -671,6 +661,14 @@ var HttpError = class extends Error {
671
661
  }
672
662
  };
673
663
 
664
+ // src/pipeline/steps/parse-query.ts
665
+ function parseQuery(request, routeEntry) {
666
+ if (!routeEntry.querySchema) return void 0;
667
+ const params = Object.fromEntries(request.nextUrl.searchParams.entries());
668
+ const result = routeEntry.querySchema.safeParse(params);
669
+ return result.success ? result.data : params;
670
+ }
671
+
674
672
  // src/pipeline/flows/static/static-invoke.ts
675
673
  function invokePaidStatic(ctx, wallet, account, body, payment) {
676
674
  return runHandler(ctx, buildHandlerCtx(ctx, wallet, account, body, payment));
@@ -688,14 +686,7 @@ function buildHandlerCtx(ctx, wallet, account, body, payment) {
688
686
  wallet,
689
687
  payment,
690
688
  account,
691
- alert(level, message, alertMeta) {
692
- firePluginHook(ctx.deps.plugin, "onAlert", ctx.pluginCtx, {
693
- level,
694
- message,
695
- route: ctx.routeEntry.key,
696
- meta: alertMeta
697
- });
698
- },
689
+ alert: ctx.report,
699
690
  setVerifiedWallet: (addr) => ctx.pluginCtx.setVerifiedWallet(addr)
700
691
  };
701
692
  }
@@ -739,45 +730,14 @@ function isThenable(value) {
739
730
  return value != null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
740
731
  }
741
732
 
742
- // src/pipeline/context/fire-provider-quota.ts
743
- function fireProviderQuota(ctx, response, handlerResult) {
744
- const { providerName, providerConfig } = ctx.routeEntry;
745
- if (!providerName || !providerConfig?.extractQuota) return;
746
- if (response.status >= 400) return;
747
- try {
748
- const quota = providerConfig.extractQuota(handlerResult, response.headers);
749
- if (!quota) return;
750
- const level = computeQuotaLevel(quota.remaining, providerConfig.warn, providerConfig.critical);
751
- const overage = providerConfig.overage ?? "same-rate";
752
- const event = {
753
- provider: providerName,
754
- route: ctx.routeEntry.key,
755
- remaining: quota.remaining,
756
- limit: quota.limit,
757
- spend: quota.spend,
758
- level,
759
- overage,
760
- message: quota.remaining !== null ? `${providerName}: ${quota.remaining}${quota.limit ? `/${quota.limit}` : ""} remaining` : `${providerName}: quota info unavailable`
761
- };
762
- firePluginHook(ctx.deps.plugin, "onProviderQuota", ctx.pluginCtx, event);
763
- } catch {
764
- }
765
- }
766
- function computeQuotaLevel(remaining, warn, critical) {
767
- if (remaining === null) return "healthy";
768
- if (critical !== void 0 && remaining <= critical) return "critical";
769
- if (warn !== void 0 && remaining <= warn) return "warn";
770
- return "healthy";
771
- }
772
-
773
- // src/pipeline/context/finalize/response.ts
733
+ // src/pipeline/steps/finalize/response.ts
774
734
  function finalize(ctx, response, rawResult, requestBody) {
775
735
  fireProviderQuota(ctx, response, rawResult);
776
736
  firePluginResponse(ctx, response, requestBody, rawResult);
777
737
  return response;
778
738
  }
779
739
 
780
- // src/pipeline/context/grant-entitlement.ts
740
+ // src/pipeline/steps/grant-entitlement.ts
781
741
  async function grantEntitlementIfSiwx(ctx, wallet) {
782
742
  if (!ctx.routeEntry.siwxEnabled) return;
783
743
  try {
@@ -790,7 +750,7 @@ async function grantEntitlementIfSiwx(ctx, wallet) {
790
750
  }
791
751
  }
792
752
 
793
- // src/pipeline/context/settlement-context.ts
753
+ // src/pipeline/steps/settlement-context.ts
794
754
  function settlementContext(ctx, scope) {
795
755
  return {
796
756
  route: ctx.routeEntry.key,
@@ -804,7 +764,7 @@ function settlementContext(ctx, scope) {
804
764
  };
805
765
  }
806
766
 
807
- // src/pipeline/context/run-settlement-error.ts
767
+ // src/pipeline/steps/run-settlement-error.ts
808
768
  async function runSettlementError(ctx, scope, error, phase) {
809
769
  const hook = ctx.routeEntry.settlement?.onSettlementError;
810
770
  if (!hook) return;
@@ -816,7 +776,7 @@ async function runSettlementError(ctx, scope, error, phase) {
816
776
  }
817
777
  }
818
778
 
819
- // src/pipeline/context/run-after-settle.ts
779
+ // src/pipeline/steps/run-after-settle.ts
820
780
  async function runAfterSettle(ctx, scope) {
821
781
  const hook = ctx.routeEntry.settlement?.afterSettle;
822
782
  if (!hook) return;
@@ -829,11 +789,11 @@ async function runAfterSettle(ctx, scope) {
829
789
  }
830
790
  }
831
791
 
832
- // src/pipeline/context/finalize/epilogue.ts
792
+ // src/pipeline/steps/finalize/epilogue.ts
833
793
  async function runPostSettleEpilogue(args) {
834
794
  const { ctx, strategy, wallet, settle, afterSettleScope, rawResult, body } = args;
835
795
  await grantEntitlementIfSiwx(ctx, wallet);
836
- firePluginHook(ctx.deps.plugin, "onPaymentSettled", ctx.pluginCtx, {
796
+ firePaymentSettled(ctx, {
837
797
  protocol: strategy.protocol,
838
798
  payer: wallet,
839
799
  transaction: settle.settledPayment.transaction ?? "",
@@ -843,7 +803,7 @@ async function runPostSettleEpilogue(args) {
843
803
  return finalize(ctx, settle.response, rawResult, body);
844
804
  }
845
805
 
846
- // src/pipeline/context/finalize/request.ts
806
+ // src/pipeline/steps/finalize/request.ts
847
807
  async function settleAndFinalizeRequest(args) {
848
808
  const { ctx, strategy, verifyOutcome, scope, rawResult, body, billedAmount, onSettleError } = args;
849
809
  const { request, routeEntry, deps, report } = ctx;
@@ -876,7 +836,7 @@ async function settleAndFinalizeRequest(args) {
876
836
  });
877
837
  }
878
838
 
879
- // src/pipeline/context/finalize/stream.ts
839
+ // src/pipeline/steps/finalize/stream.ts
880
840
  async function settleAndFinalizeStream(args) {
881
841
  const { ctx, strategy, verifyOutcome, source, account, body, bindChannelCharge } = args;
882
842
  const { request, routeEntry, deps, report } = ctx;
@@ -914,7 +874,7 @@ async function settleAndFinalizeStream(args) {
914
874
  });
915
875
  }
916
876
 
917
- // src/pipeline/context/run-handler-only.ts
877
+ // src/pipeline/steps/run-handler-only.ts
918
878
  async function runHandlerOnly(ctx, wallet, account) {
919
879
  const body = await parseBody(ctx);
920
880
  if (!body.ok) return body.response;
@@ -924,7 +884,7 @@ async function runHandlerOnly(ctx, wallet, account) {
924
884
  return finalize(ctx, result.response, result.rawResult, body.data);
925
885
  }
926
886
 
927
- // src/pipeline/context/run-before-settle.ts
887
+ // src/pipeline/steps/run-before-settle.ts
928
888
  async function runBeforeSettle(ctx, scope) {
929
889
  const hook = ctx.routeEntry.settlement?.beforeSettle;
930
890
  if (!hook) return null;
@@ -941,7 +901,7 @@ async function runBeforeSettle(ctx, scope) {
941
901
  }
942
902
  }
943
903
 
944
- // src/pipeline/context/run-settled-handler-error.ts
904
+ // src/pipeline/steps/run-settled-handler-error.ts
945
905
  async function runSettledHandlerError(ctx, scope, error = scope.handlerError ?? handlerFailureError(scope.response)) {
946
906
  const hook = ctx.routeEntry.settlement?.onSettledHandlerError;
947
907
  if (!hook) return;
@@ -1015,7 +975,7 @@ async function buildSIWXExtension() {
1015
975
  return declareSIWxExtension();
1016
976
  }
1017
977
 
1018
- // src/pipeline/context/try-siwx-fast-path.ts
978
+ // src/pipeline/steps/try-siwx-fast-path.ts
1019
979
  async function trySiwxFastPath(ctx, account) {
1020
980
  const { request, routeEntry, deps } = ctx;
1021
981
  if (!routeEntry.siwxEnabled) return null;
@@ -1027,22 +987,18 @@ async function trySiwxFastPath(ctx, account) {
1027
987
  ctx.pluginCtx.setVerifiedWallet(wallet);
1028
988
  const entitled = await deps.entitlementStore.has(routeEntry.key, wallet);
1029
989
  if (!entitled) return null;
1030
- firePluginHook(deps.plugin, "onAuthVerified", ctx.pluginCtx, {
1031
- authMode: "siwx",
1032
- wallet,
1033
- route: routeEntry.key
1034
- });
990
+ fireAuthVerified(ctx, { authMode: "siwx", wallet });
1035
991
  return runHandlerOnly(ctx, wallet, account);
1036
992
  }
1037
993
 
1038
- // src/pipeline/context/should-parse-body-early.ts
994
+ // src/pipeline/steps/should-parse-body-early.ts
1039
995
  function shouldParseBodyEarly(incomingStrategy, routeEntry, pricing) {
1040
996
  if (incomingStrategy) return false;
1041
997
  if (!routeEntry.bodySchema) return false;
1042
998
  return (pricing?.needsBody ?? false) || !!routeEntry.validateFn;
1043
999
  }
1044
1000
 
1045
- // src/pipeline/context/resolve-early-body.ts
1001
+ // src/pipeline/steps/resolve-early-body.ts
1046
1002
  async function resolveEarlyBody(args) {
1047
1003
  const { ctx, pricing, incomingStrategy } = args;
1048
1004
  if (!shouldParseBodyEarly(incomingStrategy, ctx.routeEntry, pricing)) {
@@ -1074,24 +1030,19 @@ function extractBearerToken(header) {
1074
1030
  return null;
1075
1031
  }
1076
1032
 
1077
- // src/pipeline/context/run-api-key-gate.ts
1033
+ // src/pipeline/steps/run-api-key-gate.ts
1078
1034
  async function runApiKeyGate(ctx) {
1079
- const { request, routeEntry, deps } = ctx;
1035
+ const { request, routeEntry } = ctx;
1080
1036
  if (!routeEntry.apiKeyResolver) return { ok: true, account: void 0 };
1081
1037
  const apiKeyResult = await verifyApiKey(request, routeEntry.apiKeyResolver);
1082
1038
  if (!apiKeyResult.valid) {
1083
1039
  return { ok: false, response: fail(ctx, 401, "Invalid or missing API key") };
1084
1040
  }
1085
- firePluginHook(deps.plugin, "onAuthVerified", ctx.pluginCtx, {
1086
- authMode: "apiKey",
1087
- wallet: null,
1088
- route: routeEntry.key,
1089
- account: apiKeyResult.account
1090
- });
1041
+ fireAuthVerified(ctx, { authMode: "apiKey", wallet: null, account: apiKeyResult.account });
1091
1042
  return { ok: true, account: apiKeyResult.account };
1092
1043
  }
1093
1044
 
1094
- // src/pipeline/context/protocol-init-error.ts
1045
+ // src/pipeline/steps/protocol-init-error.ts
1095
1046
  function protocolInitError(routeEntry, deps) {
1096
1047
  if (!routeEntry.pricing) return null;
1097
1048
  const errors = [];
@@ -1114,12 +1065,7 @@ async function runApiKeyOnlyFlow(ctx) {
1114
1065
  }
1115
1066
  const result = await verifyApiKey(ctx.request, ctx.routeEntry.apiKeyResolver);
1116
1067
  if (!result.valid) return fail(ctx, 401, "Invalid or missing API key");
1117
- firePluginHook(ctx.deps.plugin, "onAuthVerified", ctx.pluginCtx, {
1118
- authMode: "apiKey",
1119
- wallet: null,
1120
- route: ctx.routeEntry.key,
1121
- account: result.account
1122
- });
1068
+ fireAuthVerified(ctx, { authMode: "apiKey", wallet: null, account: result.account });
1123
1069
  return runHandlerOnly(ctx, null, result.account);
1124
1070
  }
1125
1071
 
@@ -2209,21 +2155,6 @@ function reportSettleFailure(report, err, network) {
2209
2155
  });
2210
2156
  }
2211
2157
 
2212
- // src/protocols/detect.ts
2213
- function detectProtocol(request) {
2214
- if (request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)) {
2215
- return "x402";
2216
- }
2217
- const auth = request.headers.get(HEADERS.AUTHORIZATION);
2218
- if (auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT)) {
2219
- return "mpp";
2220
- }
2221
- if (request.headers.get(HEADERS.SIWX)) {
2222
- return "siwx";
2223
- }
2224
- return null;
2225
- }
2226
-
2227
2158
  // src/protocols/index.ts
2228
2159
  var STRATEGIES = {
2229
2160
  x402: x402Strategy,
@@ -2248,9 +2179,9 @@ async function buildChallengeExtensions(ctx) {
2248
2179
  const { routeEntry } = ctx;
2249
2180
  let extensions;
2250
2181
  try {
2251
- const { z } = await import("zod");
2182
+ const { z: z2 } = await import("zod");
2252
2183
  const { declareDiscoveryExtension } = await import("@x402/extensions/bazaar");
2253
- const toJSON = (schema) => z.toJSONSchema(schema, {
2184
+ const toJSON = (schema) => z2.toJSONSchema(schema, {
2254
2185
  target: "draft-2020-12",
2255
2186
  unrepresentable: "any"
2256
2187
  });
@@ -2271,11 +2202,10 @@ async function buildChallengeExtensions(ctx) {
2271
2202
  extensions = declareDiscoveryExtension(config);
2272
2203
  }
2273
2204
  } catch (err) {
2274
- firePluginHook(ctx.deps.plugin, "onAlert", ctx.pluginCtx, {
2275
- level: "warn",
2276
- message: `Bazaar schema generation failed: ${err instanceof Error ? err.message : String(err)}`,
2277
- route: routeEntry.key
2278
- });
2205
+ ctx.report(
2206
+ "warn",
2207
+ `Bazaar schema generation failed: ${err instanceof Error ? err.message : String(err)}`
2208
+ );
2279
2209
  }
2280
2210
  if (routeEntry.siwxEnabled) {
2281
2211
  try {
@@ -2409,7 +2339,7 @@ async function runDynamicChannelMgmtFlow(args) {
2409
2339
  return build402(ctx, pricing, parsedBody, verifyOutcome.failure);
2410
2340
  }
2411
2341
  ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
2412
- firePluginHook(deps.plugin, "onPaymentVerified", ctx.pluginCtx, {
2342
+ firePaymentVerified(ctx, {
2413
2343
  protocol: strategy.protocol,
2414
2344
  payer: verifyOutcome.wallet,
2415
2345
  amount: price,
@@ -2517,14 +2447,7 @@ async function invokeDynamic(ctx, wallet, account, body, payment) {
2517
2447
  wallet,
2518
2448
  payment,
2519
2449
  account,
2520
- alert(level, message, alertMeta) {
2521
- firePluginHook(ctx.deps.plugin, "onAlert", ctx.pluginCtx, {
2522
- level,
2523
- message,
2524
- route: ctx.routeEntry.key,
2525
- meta: alertMeta
2526
- });
2527
- },
2450
+ alert: ctx.report,
2528
2451
  setVerifiedWallet: (addr) => ctx.pluginCtx.setVerifiedWallet(addr)
2529
2452
  };
2530
2453
  const handlerCtx = chargeContext !== null ? { ...baseHandlerCtx, charge: chargeContext.charge } : baseHandlerCtx;
@@ -2589,7 +2512,7 @@ function resolveDynamicPreflight(strategy, request, routeEntry) {
2589
2512
  // src/pipeline/flows/dynamic/dynamic-request.ts
2590
2513
  async function runDynamicRequestFlow(args) {
2591
2514
  const { ctx, strategy, verifyOutcome, account, body, result } = args;
2592
- const { deps, routeEntry } = ctx;
2515
+ const { routeEntry } = ctx;
2593
2516
  const settleScope = {
2594
2517
  wallet: verifyOutcome.wallet,
2595
2518
  account,
@@ -2615,11 +2538,10 @@ async function runDynamicRequestFlow(args) {
2615
2538
  billedAmount,
2616
2539
  onSettleError: async (error, failMessage) => {
2617
2540
  await runSettlementError(ctx, settleScope, error, "settle");
2618
- firePluginHook(deps.plugin, "onAlert", ctx.pluginCtx, {
2619
- level: "critical",
2620
- message: `${strategy.protocol} ${failMessage}: ${errorMessage(error, "unknown")}`,
2621
- route: routeEntry.key
2622
- });
2541
+ ctx.report(
2542
+ "critical",
2543
+ `${strategy.protocol} ${failMessage}: ${errorMessage(error, "unknown")}`
2544
+ );
2623
2545
  }
2624
2546
  });
2625
2547
  }
@@ -2689,7 +2611,7 @@ async function runDynamicPaidFlow(ctx) {
2689
2611
  return build402(ctx, pricing, parsedBody, verifyOutcome.failure);
2690
2612
  }
2691
2613
  ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
2692
- firePluginHook(deps.plugin, "onPaymentVerified", ctx.pluginCtx, {
2614
+ firePaymentVerified(ctx, {
2693
2615
  protocol: incomingStrategy.protocol,
2694
2616
  payer: verifyOutcome.wallet,
2695
2617
  amount: price,
@@ -2755,7 +2677,6 @@ async function resolveStaticBodyAndPrice(args) {
2755
2677
  // src/pipeline/flows/static/static-request.ts
2756
2678
  async function runStaticRequestFlow(args) {
2757
2679
  const { ctx, strategy, verifyOutcome, account, body, price, result } = args;
2758
- const { deps, routeEntry } = ctx;
2759
2680
  const settleScope = {
2760
2681
  wallet: verifyOutcome.wallet,
2761
2682
  account,
@@ -2796,11 +2717,10 @@ async function runStaticRequestFlow(args) {
2796
2717
  billedAmount: price,
2797
2718
  onSettleError: async (error, failMessage) => {
2798
2719
  await runSettlementError(ctx, settleScope, error, "settle");
2799
- firePluginHook(deps.plugin, "onAlert", ctx.pluginCtx, {
2800
- level: "critical",
2801
- message: `${strategy.protocol} ${failMessage}: ${errorMessage(error, "unknown")}`,
2802
- route: routeEntry.key
2803
- });
2720
+ ctx.report(
2721
+ "critical",
2722
+ `${strategy.protocol} ${failMessage}: ${errorMessage(error, "unknown")}`
2723
+ );
2804
2724
  }
2805
2725
  });
2806
2726
  }
@@ -2846,7 +2766,7 @@ async function runStaticPaidFlow(ctx) {
2846
2766
  return build402(ctx, pricing, parsedBody, verifyOutcome.failure);
2847
2767
  }
2848
2768
  ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
2849
- firePluginHook(deps.plugin, "onPaymentVerified", ctx.pluginCtx, {
2769
+ firePaymentVerified(ctx, {
2850
2770
  protocol: incomingStrategy.protocol,
2851
2771
  payer: verifyOutcome.wallet,
2852
2772
  amount: price,
@@ -3030,6 +2950,21 @@ async function createKvMppStore(kv, options) {
3030
2950
  });
3031
2951
  }
3032
2952
 
2953
+ // src/protocols/detect.ts
2954
+ function detectProtocol(request) {
2955
+ if (request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)) {
2956
+ return "x402";
2957
+ }
2958
+ const auth = request.headers.get(HEADERS.AUTHORIZATION);
2959
+ if (auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT)) {
2960
+ return "mpp";
2961
+ }
2962
+ if (request.headers.get(HEADERS.SIWX)) {
2963
+ return "siwx";
2964
+ }
2965
+ return null;
2966
+ }
2967
+
3033
2968
  // src/protocols/mpp/siwx-mode.ts
3034
2969
  var import_mppx3 = require("mppx");
3035
2970
  async function verifyMppSiwx(request, mppx) {
@@ -3069,11 +3004,7 @@ async function runSiwxOnlyFlow(ctx) {
3069
3004
  }
3070
3005
  if (mppSiwxResult.valid) {
3071
3006
  ctx.pluginCtx.setVerifiedWallet(mppSiwxResult.wallet);
3072
- firePluginHook(deps.plugin, "onAuthVerified", ctx.pluginCtx, {
3073
- authMode: "siwx",
3074
- wallet: mppSiwxResult.wallet,
3075
- route: routeEntry.key
3076
- });
3007
+ fireAuthVerified(ctx, { authMode: "siwx", wallet: mppSiwxResult.wallet });
3077
3008
  const authResponse = await runHandlerOnly(ctx, mppSiwxResult.wallet, void 0);
3078
3009
  if (authResponse.status < 400) {
3079
3010
  return mppSiwxResult.withReceipt(authResponse);
@@ -3095,11 +3026,7 @@ async function runSiwxOnlyFlow(ctx) {
3095
3026
  }
3096
3027
  const wallet = normalizeWalletAddress(siwx.wallet);
3097
3028
  ctx.pluginCtx.setVerifiedWallet(wallet);
3098
- firePluginHook(deps.plugin, "onAuthVerified", ctx.pluginCtx, {
3099
- authMode: "siwx",
3100
- wallet,
3101
- route: routeEntry.key
3102
- });
3029
+ fireAuthVerified(ctx, { authMode: "siwx", wallet });
3103
3030
  return runHandlerOnly(ctx, wallet, void 0);
3104
3031
  }
3105
3032
  async function buildSiwxChallenge(ctx) {
@@ -3192,7 +3119,7 @@ async function runUnprotectedFlow(ctx) {
3192
3119
  return runHandlerOnly(ctx, null, void 0);
3193
3120
  }
3194
3121
 
3195
- // src/orchestrate.ts
3122
+ // src/pipeline/orchestrate.ts
3196
3123
  function createRequestHandler(routeEntry, handler, deps) {
3197
3124
  return async (request) => {
3198
3125
  await deps.initPromise;
@@ -3967,7 +3894,7 @@ function toProtocolObject(protocol, mppInfo) {
3967
3894
  mpp: {
3968
3895
  method: mppInfo?.method ?? "tempo",
3969
3896
  intent: mppInfo?.intent ?? "charge",
3970
- currency: mppInfo?.currency ?? TEMPO_USDC_CURRENCY
3897
+ currency: mppInfo?.currency ?? TEMPO_USDC_ADDRESS
3971
3898
  }
3972
3899
  };
3973
3900
  }
@@ -4047,142 +3974,256 @@ function formatRouterConfigIssues(issues) {
4047
3974
  return issues.map((issue) => issue.message).join("\n");
4048
3975
  }
4049
3976
 
4050
- // src/config/validators/x402.ts
4051
- init_accepts();
3977
+ // src/config/schema.ts
3978
+ var import_zod = require("zod");
3979
+ init_constants();
4052
3980
 
4053
- // src/config/validators/shared.ts
4054
- init_evm();
4055
- init_solana();
4056
- init_accepts();
4057
- function isEvmAddress(value) {
4058
- return /^0x[a-fA-F0-9]{40}$/.test(value);
4059
- }
4060
- function isEvmPrivateKey(value) {
4061
- return /^0x[a-fA-F0-9]{64}$/.test(value);
4062
- }
4063
- function isSupportedX402Network(network) {
4064
- return isEvmNetwork(network) || isSolanaNetwork(network);
4065
- }
4066
- function findPlaceholderPayee(values) {
4067
- return values.find((value) => value !== void 0 && /^0x0{40}$/i.test(value)) ?? null;
4068
- }
4069
- function usesDefaultEvmFacilitator(config) {
4070
- return getConfiguredX402Networks(config).some(
4071
- (network) => typeof network === "string" && isEvmNetwork(network)
4072
- ) && config.x402?.facilitators?.evm === void 0;
3981
+ // src/config/utils.ts
3982
+ var import_accounts = require("viem/accounts");
3983
+ var EVM_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
3984
+ var EVM_PRIVATE_KEY_RE = /^0x[a-fA-F0-9]{64}$/;
3985
+ var SOLANA_ADDRESS_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
3986
+ var ZERO_EVM_ADDRESS_RE = /^0x0{40}$/i;
3987
+ function isUrl(value) {
3988
+ try {
3989
+ new URL(value);
3990
+ return true;
3991
+ } catch {
3992
+ return false;
3993
+ }
3994
+ }
3995
+ var isEvmAddress = (v) => EVM_ADDRESS_RE.test(v);
3996
+ var isEvmPrivateKey = (v) => EVM_PRIVATE_KEY_RE.test(v);
3997
+ var isPlaceholderEvm = (v) => ZERO_EVM_ADDRESS_RE.test(v);
3998
+ var isSolanaAddress = (v) => SOLANA_ADDRESS_RE.test(v);
3999
+ var isX402Network = (v) => v.startsWith("eip155:") || v.startsWith("solana:");
4000
+ var canonicalizeEvm = (addr) => addr.toLowerCase();
4001
+ function operatorAddressesCollide(opKey, fpKey) {
4002
+ if (!opKey || !fpKey || !isEvmPrivateKey(opKey) || !isEvmPrivateKey(fpKey)) return null;
4003
+ const op = (0, import_accounts.privateKeyToAccount)(opKey).address.toLowerCase();
4004
+ const fp = (0, import_accounts.privateKeyToAccount)(fpKey).address.toLowerCase();
4005
+ return op === fp ? op : null;
4006
+ }
4007
+ function trimAll(raw) {
4008
+ const out = {};
4009
+ for (const [k, v] of Object.entries(raw)) {
4010
+ if (typeof v !== "string") {
4011
+ out[k] = void 0;
4012
+ continue;
4013
+ }
4014
+ const trimmed = v.trim();
4015
+ out[k] = trimmed.length > 0 ? trimmed : void 0;
4016
+ }
4017
+ return out;
4073
4018
  }
4074
4019
 
4075
- // src/config/validators/x402.ts
4076
- var CHECKS = [
4077
- checkAcceptNetworkPresent,
4078
- checkAcceptNetworkSupported,
4079
- checkNonExactRequiresAsset,
4080
- checkDecimalsAreValid,
4081
- checkPayee,
4082
- checkPlaceholderPayeeAddress,
4083
- checkCdpKeys
4084
- ];
4085
- function validateX402Config(config, env, options) {
4086
- const accepts = getConfiguredX402Accepts(config);
4087
- if (accepts.length === 0) {
4020
+ // src/config/schema.ts
4021
+ function addIssue(ctx, params, message, path = []) {
4022
+ ctx.addIssue({ code: "custom", path, params, message });
4023
+ }
4024
+ var x402 = { protocol: "x402" };
4025
+ var mpp = { protocol: "mpp" };
4026
+ var envShape = {
4027
+ BASE_URL: import_zod.z.string().refine(isUrl, {
4028
+ params: { code: "invalid_base_url" },
4029
+ message: "BASE_URL must be a valid URL \u2014 the public origin used as the 402 realm, OpenAPI server URL, and MPP memo prefix. Must match the public domain."
4030
+ }).optional(),
4031
+ EVM_PAYEE_ADDRESS: import_zod.z.string().refine(isEvmAddress, {
4032
+ params: { code: "invalid_x402_payee", ...x402 },
4033
+ message: "EVM_PAYEE_ADDRESS must be a 0x-prefixed 20-byte EVM address \u2014 the wallet that receives x402 and MPP payments."
4034
+ }).refine((v) => !isPlaceholderEvm(v), {
4035
+ params: { code: "placeholder_payee", ...x402 },
4036
+ message: "EVM_PAYEE_ADDRESS is the zero address (0x000\u2026000) \u2014 payments to this address are unrecoverable. Set it to a wallet you control."
4037
+ }).optional(),
4038
+ CDP_API_KEY_ID: import_zod.z.string().optional(),
4039
+ CDP_API_KEY_SECRET: import_zod.z.string().optional(),
4040
+ SOLANA_PAYEE_ADDRESS: import_zod.z.string().refine(isSolanaAddress, {
4041
+ params: { code: "invalid_solana_payee", ...x402 },
4042
+ message: "SOLANA_PAYEE_ADDRESS must be a base58 Solana address (32\u201344 chars). When set, the router also accepts Solana payments."
4043
+ }).optional(),
4044
+ SOLANA_FACILITATOR_URL: import_zod.z.string().refine(isUrl, {
4045
+ params: { code: "invalid_solana_facilitator_url", ...x402 },
4046
+ message: "SOLANA_FACILITATOR_URL must be a valid URL \u2014 override for the Solana x402 facilitator. Defaults to DEFAULT_SOLANA_FACILITATOR_URL."
4047
+ }).optional(),
4048
+ MPP_SECRET_KEY: import_zod.z.string().optional(),
4049
+ MPP_CURRENCY: import_zod.z.string().refine(isEvmAddress, {
4050
+ params: { code: "invalid_mpp_currency", ...mpp },
4051
+ message: "MPP_CURRENCY must be a 0x-prefixed 20-byte Tempo currency address \u2014 the token contract MPP charges in. Use TEMPO_USDC_ADDRESS for Tempo USDC."
4052
+ }).optional(),
4053
+ TEMPO_RPC_URL: import_zod.z.string().refine(isUrl, {
4054
+ params: { code: "invalid_mpp_rpc_url", ...mpp },
4055
+ message: "TEMPO_RPC_URL must be a valid URL \u2014 authenticated Tempo JSON-RPC endpoint. Public rpc.tempo.xyz returns 401."
4056
+ }).optional(),
4057
+ MPP_OPERATOR_KEY: import_zod.z.string().refine(isEvmPrivateKey, {
4058
+ params: { code: "invalid_mpp_operator_key", ...mpp },
4059
+ message: "MPP_OPERATOR_KEY must be a 0x-prefixed 32-byte EVM private key \u2014 signs server-side close/settle; presence enables MPP session mode."
4060
+ }).optional(),
4061
+ MPP_FEE_PAYER_KEY: import_zod.z.string().refine(isEvmPrivateKey, {
4062
+ params: { code: "invalid_mpp_fee_payer_key", ...mpp },
4063
+ message: "MPP_FEE_PAYER_KEY must be a 0x-prefixed 32-byte EVM private key \u2014 sponsors client gas for channel open/topUp. Must resolve to a different address than MPP_OPERATOR_KEY."
4064
+ }).optional(),
4065
+ KV_REST_API_URL: import_zod.z.string().optional(),
4066
+ KV_REST_API_TOKEN: import_zod.z.string().optional(),
4067
+ NODE_ENV: import_zod.z.string().optional()
4068
+ };
4069
+ var ENV_KEYS = Object.keys(envShape);
4070
+ var EnvInputSchema = import_zod.z.object(envShape).passthrough().superRefine((env, ctx) => {
4071
+ if (env.BASE_URL === void 0) {
4072
+ addIssue(
4073
+ ctx,
4074
+ { code: "missing_base_url" },
4075
+ "BASE_URL is required \u2014 the public origin used as the 402 realm, OpenAPI server URL, and MPP memo prefix. Set it to your production domain.",
4076
+ ["BASE_URL"]
4077
+ );
4078
+ }
4079
+ if (env.EVM_PAYEE_ADDRESS === void 0) {
4080
+ addIssue(
4081
+ ctx,
4082
+ { code: "missing_x402_payee", ...x402 },
4083
+ "EVM_PAYEE_ADDRESS is required \u2014 the EVM address that receives x402 and MPP payments.",
4084
+ ["EVM_PAYEE_ADDRESS"]
4085
+ );
4086
+ }
4087
+ if (env.MPP_SECRET_KEY) {
4088
+ if (env.MPP_CURRENCY === void 0) {
4089
+ addIssue(
4090
+ ctx,
4091
+ { code: "missing_mpp_currency", ...mpp },
4092
+ "MPP_CURRENCY is required when MPP is enabled \u2014 the Tempo currency address MPP charges in. Use TEMPO_USDC_ADDRESS for Tempo USDC.",
4093
+ ["MPP_CURRENCY"]
4094
+ );
4095
+ }
4096
+ if (env.TEMPO_RPC_URL === void 0) {
4097
+ addIssue(
4098
+ ctx,
4099
+ { code: "missing_mpp_rpc_url", ...mpp },
4100
+ "TEMPO_RPC_URL is required when MPP is enabled \u2014 authenticated Tempo JSON-RPC endpoint. Public rpc.tempo.xyz returns 401.",
4101
+ ["TEMPO_RPC_URL"]
4102
+ );
4103
+ }
4104
+ }
4105
+ const collision = operatorAddressesCollide(env.MPP_OPERATOR_KEY, env.MPP_FEE_PAYER_KEY);
4106
+ if (collision) {
4107
+ addIssue(
4108
+ ctx,
4109
+ { code: "mpp_operator_equals_fee_payer", ...mpp },
4110
+ `MPP_OPERATOR_KEY and MPP_FEE_PAYER_KEY resolve to the same address (${collision}). Tempo rejects fee-delegated txs with sender === feePayer. Use two distinct wallets, or unset MPP_FEE_PAYER_KEY to let clients pay their own gas.`,
4111
+ ["MPP_FEE_PAYER_KEY"]
4112
+ );
4113
+ }
4114
+ });
4115
+ function collectKvWarnings(env, kvStoreOptionProvided) {
4116
+ if (kvStoreOptionProvided) return [];
4117
+ const warn = (code, message) => ({
4118
+ code,
4119
+ severity: "warning",
4120
+ message
4121
+ });
4122
+ if (env.KV_REST_API_URL && !env.KV_REST_API_TOKEN) {
4088
4123
  return [
4089
- {
4090
- code: "missing_x402_accepts",
4091
- protocol: "x402",
4092
- message: "x402 requires at least one accept configuration."
4093
- }
4124
+ warn(
4125
+ "kv_url_without_token",
4126
+ "KV_REST_API_URL is set but KV_REST_API_TOKEN is missing \u2014 falling back to in-memory KV (unsafe in serverless production)."
4127
+ )
4094
4128
  ];
4095
4129
  }
4096
- const args = { config, accepts, env, options };
4097
- return CHECKS.map((check) => check(args)).filter(
4098
- (issue) => issue !== null
4099
- );
4100
- }
4101
- function checkAcceptNetworkPresent({ accepts }) {
4102
- return accepts.some((accept) => !accept.network) ? { code: "missing_x402_network", protocol: "x402", message: "x402 accepts require a network." } : null;
4103
- }
4104
- function checkAcceptNetworkSupported({ accepts }) {
4105
- const unsupported = accepts.find(
4106
- (accept) => accept.network && !isSupportedX402Network(accept.network)
4107
- );
4108
- if (!unsupported) return null;
4109
- return {
4110
- code: "unsupported_x402_network",
4111
- protocol: "x402",
4112
- message: `unsupported x402 network '${unsupported.network}'. Use eip155:* or solana:*.`
4113
- };
4114
- }
4115
- function checkNonExactRequiresAsset({ accepts }) {
4116
- return accepts.some((accept) => (accept.scheme ?? "exact") !== "exact" && !accept.asset) ? {
4117
- code: "missing_x402_asset",
4118
- protocol: "x402",
4119
- message: "non-exact x402 accepts require an asset."
4120
- } : null;
4130
+ if (env.KV_REST_API_TOKEN && !env.KV_REST_API_URL) {
4131
+ return [
4132
+ warn(
4133
+ "kv_token_without_url",
4134
+ "KV_REST_API_TOKEN is set but KV_REST_API_URL is missing \u2014 falling back to in-memory KV (unsafe in serverless production)."
4135
+ )
4136
+ ];
4137
+ }
4138
+ if (env.KV_REST_API_URL && env.KV_REST_API_TOKEN && !isUrl(env.KV_REST_API_URL)) {
4139
+ return [
4140
+ warn(
4141
+ "invalid_kv_url",
4142
+ `KV_REST_API_URL is not a valid URL \u2014 KV calls will fail at request time. Got: ${env.KV_REST_API_URL}`
4143
+ )
4144
+ ];
4145
+ }
4146
+ if (!env.KV_REST_API_URL && !env.KV_REST_API_TOKEN && env.NODE_ENV === "production") {
4147
+ return [
4148
+ warn(
4149
+ "missing_kv_in_production",
4150
+ "No KV_REST_API_URL/KV_REST_API_TOKEN set in production \u2014 using the in-memory KV store. SIWX nonce, SIWX entitlement, and MPP replay state will be lost across instances. Configure Upstash/Vercel KV or pass a custom kvStore."
4151
+ )
4152
+ ];
4153
+ }
4154
+ return [];
4121
4155
  }
4122
- function checkDecimalsAreValid({ accepts }) {
4123
- const invalid = accepts.find(
4124
- (accept) => accept.decimals !== void 0 && (!Number.isInteger(accept.decimals) || accept.decimals < 0)
4125
- );
4126
- if (!invalid) return null;
4127
- return {
4128
- code: "invalid_x402_decimals",
4129
- protocol: "x402",
4130
- message: "x402 accept decimals must be a non-negative integer."
4131
- };
4156
+ function getConfiguredX402Accepts2(config) {
4157
+ if (config.x402?.accepts?.length) return [...config.x402.accepts];
4158
+ return [
4159
+ {
4160
+ scheme: "exact",
4161
+ network: config.network ?? BASE_MAINNET_NETWORK,
4162
+ payTo: config.payeeAddress
4163
+ }
4164
+ ];
4132
4165
  }
4133
- function checkPayee({ config, accepts }) {
4134
- if (config.payeeAddress) return null;
4135
- return accepts.some((accept) => !accept.payTo) ? {
4136
- code: "missing_x402_payee",
4137
- protocol: "x402",
4138
- message: "x402 requires payeeAddress in router config or payTo on every x402 accept."
4139
- } : null;
4140
- }
4141
- function checkPlaceholderPayeeAddress({
4142
- config,
4143
- accepts
4144
- }) {
4145
- const placeholder = findPlaceholderPayee([
4166
+ function validateX402Config(config, env, options) {
4167
+ const accepts = getConfiguredX402Accepts2(config);
4168
+ const issues = [];
4169
+ const push = (code, message) => issues.push({ code, protocol: "x402", message });
4170
+ if (accepts.length === 0) {
4171
+ push("missing_x402_accepts", "x402 requires at least one accept configuration.");
4172
+ return issues;
4173
+ }
4174
+ if (accepts.some((a) => !a.network)) {
4175
+ push("missing_x402_network", "x402 accepts require a network.");
4176
+ }
4177
+ const unsupported = accepts.find((a) => a.network && !isX402Network(a.network));
4178
+ if (unsupported) {
4179
+ push(
4180
+ "unsupported_x402_network",
4181
+ `unsupported x402 network '${unsupported.network}'. Use eip155:* or solana:*.`
4182
+ );
4183
+ }
4184
+ if (accepts.some((a) => (a.scheme ?? "exact") !== "exact" && !a.asset)) {
4185
+ push("missing_x402_asset", "non-exact x402 accepts require an asset.");
4186
+ }
4187
+ if (accepts.some(
4188
+ (a) => a.decimals !== void 0 && (!Number.isInteger(a.decimals) || a.decimals < 0)
4189
+ )) {
4190
+ push("invalid_x402_decimals", "x402 accept decimals must be a non-negative integer.");
4191
+ }
4192
+ if (!config.payeeAddress && accepts.some((a) => !a.payTo)) {
4193
+ push(
4194
+ "missing_x402_payee",
4195
+ "x402 requires payeeAddress in router config or payTo on every x402 accept."
4196
+ );
4197
+ }
4198
+ const placeholder = [
4146
4199
  config.payeeAddress,
4147
- ...accepts.map((accept) => typeof accept.payTo === "string" ? accept.payTo : void 0)
4148
- ]);
4149
- if (!placeholder) return null;
4150
- return {
4151
- code: "placeholder_payee",
4152
- protocol: "x402",
4153
- message: `x402 payee '${placeholder}' is a placeholder address and cannot receive payments.`
4154
- };
4155
- }
4156
- function checkCdpKeys({ config, env, options }) {
4157
- if (options.requireCdpKeys === false) return null;
4158
- if (!usesDefaultEvmFacilitator(config)) return null;
4159
- const missing = [
4160
- env.CDP_API_KEY_ID ? null : "CDP_API_KEY_ID",
4161
- env.CDP_API_KEY_SECRET ? null : "CDP_API_KEY_SECRET"
4162
- ].filter(Boolean);
4163
- if (missing.length === 0) return null;
4164
- return {
4165
- code: "missing_cdp_keys",
4166
- protocol: "x402",
4167
- message: `default EVM x402 facilitator requires ${missing.join(" and ")}.`
4168
- };
4200
+ ...accepts.map((a) => typeof a.payTo === "string" ? a.payTo : void 0)
4201
+ ].find((v) => v !== void 0 && isPlaceholderEvm(v));
4202
+ if (placeholder) {
4203
+ push(
4204
+ "placeholder_payee",
4205
+ `x402 payee '${placeholder}' is a placeholder address and cannot receive payments.`
4206
+ );
4207
+ }
4208
+ if (options.requireCdpKeys !== false) {
4209
+ const hasEvm = accepts.some(
4210
+ (a) => typeof a.network === "string" && a.network.startsWith("eip155:")
4211
+ );
4212
+ if (hasEvm) {
4213
+ const missing = ["CDP_API_KEY_ID", "CDP_API_KEY_SECRET"].filter((k) => !env[k]);
4214
+ if (missing.length > 0) {
4215
+ push(
4216
+ "missing_cdp_keys",
4217
+ `x402 EVM facilitator (Coinbase) requires ${missing.join(" and ")}.`
4218
+ );
4219
+ }
4220
+ }
4221
+ }
4222
+ return issues;
4169
4223
  }
4170
-
4171
- // src/config/validators/mpp.ts
4172
- var import_accounts = require("viem/accounts");
4173
- var CHECKS2 = [
4174
- checkSecretKey,
4175
- checkCurrency,
4176
- checkRecipient,
4177
- checkPlaceholderRecipient,
4178
- checkRpcUrl,
4179
- checkFeePayerKey,
4180
- checkOperatorKey,
4181
- checkOperatorMatchesFeePayer
4182
- ];
4183
- function validateMppConfig(config, env) {
4184
- const mpp = config.mpp;
4185
- if (!mpp) {
4224
+ function validateMppConfig(config) {
4225
+ const m = config.mpp;
4226
+ if (!m) {
4186
4227
  return [
4187
4228
  {
4188
4229
  code: "missing_mpp_config",
@@ -4191,109 +4232,184 @@ function validateMppConfig(config, env) {
4191
4232
  }
4192
4233
  ];
4193
4234
  }
4194
- const args = { config, mpp, env };
4195
- return CHECKS2.map((check) => check(args)).filter(
4196
- (issue) => issue !== null
4235
+ const issues = [];
4236
+ const push = (code, message) => issues.push({ code, protocol: "mpp", message });
4237
+ if (!m.secretKey) {
4238
+ push(
4239
+ "missing_mpp_secret_key",
4240
+ "MPP requires secretKey. Set MPP_SECRET_KEY or pass mpp.secretKey."
4241
+ );
4242
+ }
4243
+ if (!m.currency) {
4244
+ push("missing_mpp_currency", "MPP requires currency. Set MPP_CURRENCY or pass mpp.currency.");
4245
+ } else if (!isEvmAddress(m.currency)) {
4246
+ push(
4247
+ "invalid_mpp_currency",
4248
+ "MPP currency must be a 0x-prefixed 20-byte Tempo currency address. Use TEMPO_USDC_ADDRESS for Tempo USDC."
4249
+ );
4250
+ }
4251
+ const recipient = m.recipient ?? config.payeeAddress;
4252
+ if (!recipient) {
4253
+ push(
4254
+ "missing_mpp_recipient",
4255
+ "MPP requires a recipient address. Set mpp.recipient or payeeAddress in your router config."
4256
+ );
4257
+ } else if (!isEvmAddress(recipient)) {
4258
+ push(
4259
+ "invalid_mpp_recipient",
4260
+ "MPP recipient must be a 0x-prefixed EVM address. Solana recipients require x402."
4261
+ );
4262
+ }
4263
+ const placeholder = [m.recipient, config.payeeAddress].find(
4264
+ (v) => typeof v === "string" && isPlaceholderEvm(v)
4197
4265
  );
4198
- }
4199
- function checkSecretKey({ mpp }) {
4200
- if (mpp.secretKey) return null;
4201
- return {
4202
- code: "missing_mpp_secret_key",
4203
- protocol: "mpp",
4204
- message: "MPP requires secretKey. Set MPP_SECRET_KEY or pass mpp.secretKey."
4205
- };
4206
- }
4207
- function checkCurrency({ mpp }) {
4208
- if (!mpp.currency) {
4209
- return {
4210
- code: "missing_mpp_currency",
4211
- protocol: "mpp",
4212
- message: "MPP requires currency. Set MPP_CURRENCY or pass mpp.currency."
4213
- };
4266
+ if (placeholder) {
4267
+ push(
4268
+ "placeholder_payee",
4269
+ `MPP recipient '${placeholder}' is a placeholder address and cannot receive payments.`
4270
+ );
4214
4271
  }
4215
- if (!isEvmAddress(mpp.currency)) {
4216
- return {
4217
- code: "invalid_mpp_currency",
4218
- protocol: "mpp",
4219
- message: "MPP currency must be a 0x-prefixed 20-byte Tempo currency address. Use TEMPO_USDC_CURRENCY for Tempo USDC."
4220
- };
4272
+ if (!m.rpcUrl) {
4273
+ push(
4274
+ "missing_mpp_rpc_url",
4275
+ "MPP requires an authenticated Tempo RPC URL. Set TEMPO_RPC_URL env var or pass rpcUrl in the mpp config object."
4276
+ );
4221
4277
  }
4222
- return null;
4278
+ if (m.feePayerKey && !isEvmPrivateKey(m.feePayerKey)) {
4279
+ push(
4280
+ "invalid_mpp_fee_payer_key",
4281
+ "MPP feePayerKey must be a 0x-prefixed 32-byte EVM private key."
4282
+ );
4283
+ }
4284
+ if (m.operatorKey && !isEvmPrivateKey(m.operatorKey)) {
4285
+ push(
4286
+ "invalid_mpp_operator_key",
4287
+ "MPP operatorKey must be a 0x-prefixed 32-byte EVM private key."
4288
+ );
4289
+ }
4290
+ const collision = operatorAddressesCollide(m.operatorKey, m.feePayerKey);
4291
+ if (collision) {
4292
+ push(
4293
+ "mpp_operator_equals_fee_payer",
4294
+ `MPP operatorKey and feePayerKey resolve to the same address (${collision}). Tempo rejects fee-delegated txs with sender === feePayer, so channel close/settle would fail at runtime. Either use two distinct wallets, or omit feePayerKey to disable gas sponsorship (clients then pay their own gas).`
4295
+ );
4296
+ }
4297
+ return issues;
4223
4298
  }
4224
- function checkRecipient({ config, mpp }) {
4225
- const recipient = mpp.recipient ?? config.payeeAddress;
4226
- if (!recipient) {
4299
+ function translateZodIssues(error) {
4300
+ return error.issues.map((issue) => {
4301
+ const params = issue.params;
4302
+ if (!params?.code) {
4303
+ throw new Error(
4304
+ `[router] schema issue missing params.code (path=${issue.path.join(".")}, message=${issue.message}). Every refinement / addIssue call must set params.code.`
4305
+ );
4306
+ }
4227
4307
  return {
4228
- code: "missing_mpp_recipient",
4229
- protocol: "mpp",
4230
- message: "MPP requires a recipient address. Set mpp.recipient or payeeAddress in your router config."
4308
+ code: params.code,
4309
+ message: issue.message,
4310
+ ...params.protocol ? { protocol: params.protocol } : {},
4311
+ ...params.severity ? { severity: params.severity } : {}
4231
4312
  };
4313
+ });
4314
+ }
4315
+ function routerConfigFromEnv(options) {
4316
+ const rawEnv = options.env ?? process.env;
4317
+ const env = trimAll(rawEnv);
4318
+ const optionIssues = [];
4319
+ if (!options.title?.trim()) {
4320
+ optionIssues.push({
4321
+ code: "missing_discovery_title",
4322
+ message: "discovery `title` is required. Pass a short product name."
4323
+ });
4232
4324
  }
4233
- if (!isEvmAddress(recipient)) {
4234
- return {
4235
- code: "invalid_mpp_recipient",
4236
- protocol: "mpp",
4237
- message: "MPP recipient must be a 0x-prefixed EVM address. Solana recipients require x402."
4238
- };
4325
+ if (!options.description?.trim()) {
4326
+ optionIssues.push({
4327
+ code: "missing_discovery_description",
4328
+ message: "discovery `description` is required. One sentence is enough."
4329
+ });
4239
4330
  }
4240
- return null;
4241
- }
4242
- function checkPlaceholderRecipient({ config, mpp }) {
4243
- const placeholder = findPlaceholderPayee([mpp.recipient, config.payeeAddress]);
4244
- if (!placeholder) return null;
4245
- return {
4246
- code: "placeholder_payee",
4247
- protocol: "mpp",
4248
- message: `MPP recipient '${placeholder}' is a placeholder address and cannot receive payments.`
4249
- };
4250
- }
4251
- function checkRpcUrl({ mpp, env }) {
4252
- if (mpp.rpcUrl ?? env.TEMPO_RPC_URL) return null;
4253
- return {
4254
- code: "missing_mpp_rpc_url",
4255
- protocol: "mpp",
4256
- message: "MPP requires an authenticated Tempo RPC URL. Set TEMPO_RPC_URL env var or pass rpcUrl in the mpp config object."
4257
- };
4258
- }
4259
- function checkFeePayerKey({ mpp }) {
4260
- if (!mpp.feePayerKey || isEvmPrivateKey(mpp.feePayerKey)) return null;
4261
- return {
4262
- code: "invalid_mpp_fee_payer_key",
4263
- protocol: "mpp",
4264
- message: "MPP feePayerKey must be a 0x-prefixed 32-byte EVM private key."
4265
- };
4266
- }
4267
- function checkOperatorKey({ mpp }) {
4268
- if (!mpp.operatorKey || isEvmPrivateKey(mpp.operatorKey)) return null;
4269
- return {
4270
- code: "invalid_mpp_operator_key",
4271
- protocol: "mpp",
4272
- message: "MPP operatorKey must be a 0x-prefixed 32-byte EVM private key."
4273
- };
4274
- }
4275
- function checkOperatorMatchesFeePayer({ mpp }) {
4276
- if (!mpp.operatorKey || !mpp.feePayerKey) return null;
4277
- if (!isEvmPrivateKey(mpp.operatorKey) || !isEvmPrivateKey(mpp.feePayerKey)) return null;
4278
- const opAddr = (0, import_accounts.privateKeyToAccount)(mpp.operatorKey).address.toLowerCase();
4279
- const fpAddr = (0, import_accounts.privateKeyToAccount)(mpp.feePayerKey).address.toLowerCase();
4280
- if (opAddr !== fpAddr) return null;
4331
+ if (options.guidance === void 0) {
4332
+ optionIssues.push({
4333
+ code: "missing_discovery_guidance",
4334
+ message: "discovery `guidance` is required. Provide an empty string to opt out of `/llms.txt`."
4335
+ });
4336
+ }
4337
+ if (options.serverUrl !== void 0 && !isUrl(options.serverUrl)) {
4338
+ optionIssues.push({
4339
+ code: "invalid_server_url",
4340
+ message: `discovery \`serverUrl\` must be a valid URL. Got: ${options.serverUrl}`
4341
+ });
4342
+ }
4343
+ const parsed = EnvInputSchema.safeParse(env);
4344
+ const envIssues = parsed.success ? [] : translateZodIssues(parsed.error);
4345
+ const issues = [...envIssues, ...optionIssues];
4346
+ if (issues.length > 0) throw new RouterConfigError(issues);
4347
+ for (const warning of collectKvWarnings(env, options.kvStore !== void 0)) {
4348
+ console.warn(`[router] ${warning.message}`);
4349
+ }
4350
+ const payeeAddress = canonicalizeEvm(env.EVM_PAYEE_ADDRESS);
4351
+ const accepts = [
4352
+ { scheme: "exact", network: BASE_MAINNET_NETWORK, payTo: payeeAddress },
4353
+ {
4354
+ scheme: "upto",
4355
+ network: BASE_MAINNET_NETWORK,
4356
+ payTo: payeeAddress,
4357
+ asset: BASE_USDC_ADDRESS,
4358
+ decimals: BASE_USDC_DECIMALS
4359
+ }
4360
+ ];
4361
+ if (env.SOLANA_PAYEE_ADDRESS) {
4362
+ accepts.push({
4363
+ scheme: "exact",
4364
+ network: SOLANA_MAINNET_NETWORK,
4365
+ payTo: env.SOLANA_PAYEE_ADDRESS
4366
+ });
4367
+ }
4368
+ const configuredSolanaFacilitator = options.x402Facilitators?.solana;
4369
+ const solanaFacilitator = typeof configuredSolanaFacilitator === "string" ? configuredSolanaFacilitator : configuredSolanaFacilitator ?? env.SOLANA_FACILITATOR_URL ?? DEFAULT_SOLANA_FACILITATOR_URL;
4370
+ const mppEnabled = options.protocols?.includes("mpp") ?? Boolean(env.MPP_SECRET_KEY);
4371
+ const protocols = options.protocols ? [...options.protocols] : mppEnabled ? ["x402", "mpp"] : ["x402"];
4372
+ const mppConfig = mppEnabled ? {
4373
+ secretKey: env.MPP_SECRET_KEY,
4374
+ currency: canonicalizeEvm(env.MPP_CURRENCY),
4375
+ rpcUrl: env.TEMPO_RPC_URL,
4376
+ recipient: payeeAddress,
4377
+ ...env.MPP_FEE_PAYER_KEY ? { feePayerKey: env.MPP_FEE_PAYER_KEY } : {},
4378
+ ...env.MPP_OPERATOR_KEY ? { operatorKey: env.MPP_OPERATOR_KEY, session: {} } : {}
4379
+ } : void 0;
4281
4380
  return {
4282
- code: "mpp_operator_equals_fee_payer",
4283
- protocol: "mpp",
4284
- message: `MPP operatorKey and feePayerKey resolve to the same address (${opAddr}). Tempo rejects fee-delegated txs with sender === feePayer, so channel close/settle would fail at runtime. Either use two distinct wallets, or omit feePayerKey to disable gas sponsorship (clients then pay their own gas).`
4381
+ payeeAddress,
4382
+ baseUrl: env.BASE_URL,
4383
+ network: BASE_MAINNET_NETWORK,
4384
+ protocols,
4385
+ x402: {
4386
+ accepts,
4387
+ facilitators: {
4388
+ ...options.x402Facilitators,
4389
+ solana: solanaFacilitator
4390
+ }
4391
+ },
4392
+ ...mppConfig ? { mpp: mppConfig } : {},
4393
+ discovery: {
4394
+ title: options.title,
4395
+ version: options.version ?? "1.0.0",
4396
+ description: options.description,
4397
+ guidance: options.guidance,
4398
+ ...options.contact ? { contact: options.contact } : {},
4399
+ ...options.ownershipProofs ? { ownershipProofs: options.ownershipProofs } : {},
4400
+ ...options.methodHints ? { methodHints: options.methodHints } : {},
4401
+ ...options.serverUrl ? { serverUrl: options.serverUrl } : {}
4402
+ },
4403
+ ...options.prices ? { prices: options.prices } : {},
4404
+ ...options.plugin ? { plugin: options.plugin } : {},
4405
+ ...options.kvStore ? { kvStore: options.kvStore } : {},
4406
+ strictRoutes: options.strictRoutes ?? false
4285
4407
  };
4286
4408
  }
4287
-
4288
- // src/config/validate.ts
4289
- function validateRouterConfig(config, options = {}) {
4290
- const issues = getRouterConfigIssues(config, options);
4291
- if (issues.length > 0) throw new RouterConfigError(issues);
4292
- }
4293
4409
  function getRouterConfigIssues(config, options = {}) {
4294
- const env = options.env ?? process.env;
4295
- const issues = [];
4410
+ const env = options.env ?? {};
4296
4411
  const protocols = config.protocols ?? ["x402"];
4412
+ const issues = [];
4297
4413
  if (!config.baseUrl) {
4298
4414
  issues.push({
4299
4415
  code: "missing_base_url",
@@ -4306,83 +4422,16 @@ function getRouterConfigIssues(config, options = {}) {
4306
4422
  message: "RouterConfig.protocols cannot be empty. Omit the field to use default ['x402'] or specify protocols explicitly."
4307
4423
  });
4308
4424
  }
4309
- if (protocols.includes("x402")) {
4310
- issues.push(...validateX402Config(config, env, options));
4311
- }
4312
- if (protocols.includes("mpp")) {
4313
- issues.push(...validateMppConfig(config, env));
4314
- }
4425
+ if (protocols.includes("x402")) issues.push(...validateX402Config(config, env, options));
4426
+ if (protocols.includes("mpp")) issues.push(...validateMppConfig(config));
4315
4427
  return issues;
4316
4428
  }
4317
4429
 
4318
- // src/config/env.ts
4319
- init_constants();
4320
- function mppFromEnv(env, options = {}) {
4321
- const secretKey = env.MPP_SECRET_KEY;
4322
- const currency = env.MPP_CURRENCY;
4323
- const rpcUrl = env.TEMPO_RPC_URL;
4324
- const feePayerKey = options.feePayerKey ?? env.MPP_FEE_PAYER_KEY;
4325
- const feePayerKeySource = options.feePayerKey !== void 0 ? "feePayerKey" : "MPP_FEE_PAYER_KEY";
4326
- const hasAnyMppEnv = Boolean(secretKey || currency || rpcUrl || options.require);
4327
- if (!hasAnyMppEnv) return void 0;
4328
- const missing = [
4329
- secretKey ? null : "MPP_SECRET_KEY",
4330
- currency ? null : "MPP_CURRENCY",
4331
- rpcUrl ? null : "TEMPO_RPC_URL"
4332
- ].filter(Boolean);
4333
- if (missing.length > 0) {
4334
- throw new Error(`MPP env is incomplete. Missing: ${missing.join(", ")}`);
4335
- }
4336
- if (!isEvmAddress(currency)) {
4337
- throw new Error("MPP_CURRENCY must be a 0x-prefixed 20-byte Tempo currency address");
4338
- }
4339
- if (options.recipient && !isEvmAddress(options.recipient)) {
4340
- throw new Error("MPP recipient must be a 0x-prefixed EVM address");
4341
- }
4342
- if (feePayerKey && !isEvmPrivateKey(feePayerKey)) {
4343
- throw new Error(`${feePayerKeySource} must be a 0x-prefixed 32-byte EVM private key`);
4344
- }
4345
- return {
4346
- secretKey,
4347
- currency,
4348
- rpcUrl,
4349
- ...options.recipient ? { recipient: options.recipient } : {},
4350
- ...feePayerKey ? { feePayerKey } : {}
4351
- };
4352
- }
4353
- function x402AcceptsFromEnv(env, options = {}) {
4354
- const payeeEnv = options.payeeEnv ?? "X402_WALLET_ADDRESS";
4355
- const solanaPayeeEnv = options.solanaPayeeEnv ?? "SOLANA_PAYEE_ADDRESS";
4356
- const payeeAddress = options.payeeAddress ?? env[payeeEnv];
4357
- if (!payeeAddress) {
4358
- throw new Error(`${payeeEnv} is required to build x402 accepts`);
4359
- }
4360
- const accepts = [
4361
- {
4362
- scheme: "exact",
4363
- network: options.network ?? BASE_NETWORK,
4364
- payTo: payeeAddress
4365
- }
4366
- ];
4367
- const solanaPayeeAddress = options.solanaPayeeAddress ?? env[solanaPayeeEnv];
4368
- if (solanaPayeeAddress) {
4369
- accepts.push({
4370
- scheme: "exact",
4371
- network: SOLANA_MAINNET_NETWORK,
4372
- payTo: solanaPayeeAddress
4373
- });
4374
- }
4375
- return accepts;
4376
- }
4377
- function paidOptionsForProtocols(protocols) {
4378
- return { protocols: [...protocols] };
4379
- }
4380
-
4381
4430
  // src/init/x402.ts
4382
4431
  async function initX402(config, configError) {
4383
4432
  if (configError) return { initError: configError };
4384
4433
  try {
4385
- const { createX402Server: createX402Server2 } = await Promise.resolve().then(() => (init_server(), server_exports));
4434
+ const { createX402Server: createX402Server2 } = await Promise.resolve().then(() => (init_x402_server(), x402_server_exports));
4386
4435
  const result = await createX402Server2(config);
4387
4436
  await result.initPromise;
4388
4437
  return {
@@ -4394,7 +4443,7 @@ async function initX402(config, configError) {
4394
4443
  }
4395
4444
  }
4396
4445
 
4397
- // src/mppx-init.ts
4446
+ // src/init/mppx.ts
4398
4447
  function getMppxRequestContext(args) {
4399
4448
  const {
4400
4449
  Mppx,
@@ -4516,9 +4565,10 @@ function createRouter(config) {
4516
4565
  const kvStore = resolveKvStore(config.kvStore);
4517
4566
  const nonceStore = kvStore ? createKvNonceStore(kvStore) : new MemoryNonceStore();
4518
4567
  const entitlementStore = kvStore ? createKvEntitlementStore(kvStore) : new MemoryEntitlementStore();
4519
- const network = config.network ?? BASE_NETWORK;
4568
+ const network = config.network ?? BASE_MAINNET_NETWORK;
4520
4569
  const x402Accepts = getConfiguredX402Accepts(config);
4521
4570
  const configIssues = getRouterConfigIssues(config, {
4571
+ env: process.env,
4522
4572
  requireCdpKeys: process.env.NODE_ENV === "production"
4523
4573
  });
4524
4574
  const baseUrlIssue = configIssues.find((issue) => issue.code === "missing_base_url");
@@ -4641,30 +4691,22 @@ function normalizePath(path) {
4641
4691
  normalized = normalized.replace(/^api\/+/, "");
4642
4692
  return normalized.replace(/\/+$/, "");
4643
4693
  }
4694
+ function createRouterFromEnv(options) {
4695
+ return createRouter(routerConfigFromEnv(options));
4696
+ }
4644
4697
  // Annotate the CommonJS export names for ESM import in node:
4645
4698
  0 && (module.exports = {
4646
- BASE_NETWORK,
4699
+ BASE_MAINNET_NETWORK,
4700
+ BASE_USDC_ADDRESS,
4701
+ BASE_USDC_DECIMALS,
4702
+ DEFAULT_SOLANA_FACILITATOR_URL,
4647
4703
  HttpError,
4648
- MemoryEntitlementStore,
4649
- MemoryNonceStore,
4650
- RouteBuilder,
4651
- RouteRegistry,
4652
4704
  RouterConfigError,
4653
- SIWX_CHALLENGE_EXPIRY_MS,
4654
- SIWX_ERROR_MESSAGES,
4655
4705
  SOLANA_MAINNET_NETWORK,
4656
- TEMPO_USDC_CURRENCY,
4706
+ TEMPO_USDC_ADDRESS,
4707
+ TEMPO_USDC_DECIMALS,
4657
4708
  ZERO_EVM_ADDRESS,
4658
- consolePlugin,
4659
- createKvEntitlementStore,
4660
- createKvMppStore,
4661
- createKvNonceStore,
4662
4709
  createRouter,
4663
- formatRouterConfigIssues,
4664
- getRouterConfigIssues,
4665
- mppFromEnv,
4666
- paidOptionsForProtocols,
4667
- validateRouterConfig,
4668
- withPrefix,
4669
- x402AcceptsFromEnv
4710
+ createRouterFromEnv,
4711
+ routerConfigFromEnv
4670
4712
  });