@agentcash/router 1.6.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,22 +255,148 @@ 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();
264
+ }
265
+ });
266
+
267
+ // src/kv-store/facilitator-supported.ts
268
+ function withCachedSupported(inner, options = {}) {
269
+ const { kv, cacheKey, ttlSeconds = FACILITATOR_SUPPORTED_TTL_SECONDS, fallback } = options;
270
+ const kvKey = kv && cacheKey ? `${FACILITATOR_SUPPORTED_KV_PREFIX}${cacheKey}` : void 0;
271
+ let inflight;
272
+ return {
273
+ verify: inner.verify.bind(inner),
274
+ settle: inner.settle.bind(inner),
275
+ getSupported: () => {
276
+ if (inflight) return inflight;
277
+ const attempt = fetchSupported(inner, kv, kvKey, ttlSeconds, fallback);
278
+ inflight = attempt;
279
+ attempt.catch(() => {
280
+ if (inflight === attempt) inflight = void 0;
281
+ });
282
+ return attempt;
283
+ }
284
+ };
285
+ }
286
+ async function fetchSupported(inner, kv, kvKey, ttlSeconds, fallback) {
287
+ if (kv && kvKey) {
288
+ const cached = await readKvCache(kv, kvKey);
289
+ if (cached) return cached;
290
+ }
291
+ const fresh = await tryFetchLive(inner, fallback);
292
+ if (fresh === null) return fallback();
293
+ if (kv && kvKey) await writeKvCache(kv, kvKey, fresh, ttlSeconds);
294
+ return fresh;
295
+ }
296
+ async function tryFetchLive(inner, fallback) {
297
+ try {
298
+ return await inner.getSupported();
299
+ } catch (err) {
300
+ if (!fallback) throw err;
301
+ console.warn(
302
+ `[x402] facilitator /supported failed, using hardcoded baseline: ${err instanceof Error ? err.message : String(err)}`
303
+ );
304
+ return null;
305
+ }
306
+ }
307
+ async function readKvCache(kv, key) {
308
+ try {
309
+ const cached = await kv.get(key);
310
+ return isSupportedResponse(cached) ? cached : void 0;
311
+ } catch {
312
+ return void 0;
313
+ }
314
+ }
315
+ async function writeKvCache(kv, key, value, ttlSeconds) {
316
+ try {
317
+ await kv.setNxEx(key, value, ttlSeconds);
318
+ } catch {
319
+ }
320
+ }
321
+ function isSupportedResponse(value) {
322
+ return typeof value === "object" && value !== null && Array.isArray(value.kinds);
323
+ }
324
+ var FACILITATOR_SUPPORTED_TTL_SECONDS, FACILITATOR_SUPPORTED_KV_PREFIX;
325
+ var init_facilitator_supported = __esm({
326
+ "src/kv-store/facilitator-supported.ts"() {
327
+ "use strict";
328
+ FACILITATOR_SUPPORTED_TTL_SECONDS = 60 * 60;
329
+ FACILITATOR_SUPPORTED_KV_PREFIX = "x402:facilitator-supported:";
330
+ }
331
+ });
332
+
333
+ // src/protocols/x402/facilitator-clients.ts
334
+ function createFacilitatorClients(facilitatorsByNetwork, HTTPFacilitatorClient, kvStore) {
335
+ return getResolvedX402FacilitatorGroups(facilitatorsByNetwork).map((group) => {
336
+ const inner = new HTTPFacilitatorClient(group.config);
337
+ const kinds = buildSupportedKinds(group);
338
+ const baseline = () => ({
339
+ kinds,
340
+ extensions: [],
341
+ signers: {}
342
+ });
343
+ if (group.family === "solana") {
344
+ return hardcodedSupportedClient(inner, baseline);
345
+ }
346
+ const cached = withCachedSupported(inner, {
347
+ kv: kvStore,
348
+ cacheKey: group.config.url,
349
+ fallback: baseline
350
+ });
351
+ return withScopedKinds(cached, kinds);
352
+ });
353
+ }
354
+ function hardcodedSupportedClient(inner, build) {
355
+ return {
356
+ verify: inner.verify.bind(inner),
357
+ settle: inner.settle.bind(inner),
358
+ getSupported: async () => build()
359
+ };
360
+ }
361
+ function withScopedKinds(client, kinds) {
362
+ return {
363
+ verify: client.verify.bind(client),
364
+ settle: client.settle.bind(client),
365
+ getSupported: async () => ({ ...await client.getSupported(), kinds })
366
+ };
367
+ }
368
+ function buildSupportedKinds(group) {
369
+ return group.networks.flatMap((network) => {
370
+ if (group.family === "solana") {
371
+ return [
372
+ {
373
+ x402Version: 2,
374
+ scheme: "exact",
375
+ network,
376
+ extra: { features: { xSettlementAccountSupported: true } }
377
+ }
378
+ ];
379
+ }
380
+ return [
381
+ { x402Version: 2, scheme: "exact", network },
382
+ { x402Version: 2, scheme: "upto", network }
383
+ ];
384
+ });
385
+ }
386
+ var init_facilitator_clients = __esm({
387
+ "src/protocols/x402/facilitator-clients.ts"() {
388
+ "use strict";
389
+ init_facilitator_supported();
390
+ init_facilitators();
258
391
  }
259
392
  });
260
393
 
261
- // src/server.ts
262
- var server_exports = {};
263
- __export(server_exports, {
394
+ // src/init/x402-server.ts
395
+ var x402_server_exports = {};
396
+ __export(x402_server_exports, {
264
397
  createX402Server: () => createX402Server
265
398
  });
266
- async function createX402Server(config) {
399
+ async function createX402Server(config, kvStore) {
267
400
  const { x402ResourceServer, HTTPFacilitatorClient } = await import("@x402/core/server");
268
401
  const { registerExactEvmScheme } = await import("@x402/evm/exact/server");
269
402
  const { bazaarResourceServerExtension } = await import("@x402/extensions/bazaar");
@@ -277,7 +410,11 @@ async function createX402Server(config) {
277
410
  );
278
411
  const evmNetworks = filterEvmNetworks(configuredNetworks);
279
412
  const svmNetworks = filterSolanaNetworks(configuredNetworks);
280
- const facilitatorClients = createFacilitatorClients(facilitatorsByNetwork, HTTPFacilitatorClient);
413
+ const facilitatorClients = createFacilitatorClients(
414
+ facilitatorsByNetwork,
415
+ HTTPFacilitatorClient,
416
+ kvStore
417
+ );
281
418
  const server = new x402ResourceServer(
282
419
  facilitatorClients.length === 1 ? facilitatorClients[0] : facilitatorClients
283
420
  );
@@ -301,48 +438,13 @@ async function createX402Server(config) {
301
438
  facilitatorsByNetwork
302
439
  };
303
440
  }
304
- function createFacilitatorClients(facilitatorsByNetwork, HTTPFacilitatorClient) {
305
- const groups = getResolvedX402FacilitatorGroups(facilitatorsByNetwork);
306
- return groups.map((group) => {
307
- const inner = new HTTPFacilitatorClient(group.config);
308
- const kinds = buildSupportedKinds(group);
309
- return hardcodedSupportedClient(inner, kinds);
310
- });
311
- }
312
- function hardcodedSupportedClient(inner, kinds) {
313
- return {
314
- verify: inner.verify.bind(inner),
315
- settle: inner.settle.bind(inner),
316
- getSupported: async () => ({ kinds, extensions: [], signers: {} })
317
- };
318
- }
319
- function buildSupportedKinds(group) {
320
- return group.networks.flatMap((network) => {
321
- const exactKind = {
322
- x402Version: 2,
323
- scheme: "exact",
324
- network,
325
- ...group.family === "solana" ? {
326
- extra: {
327
- features: {
328
- xSettlementAccountSupported: true
329
- }
330
- }
331
- } : {}
332
- };
333
- const uptoKind = { x402Version: 2, scheme: "upto", network };
334
- if (group.family === "evm") {
335
- return [exactKind, uptoKind];
336
- }
337
- return [exactKind, uptoKind];
338
- });
339
- }
340
- var init_server = __esm({
341
- "src/server.ts"() {
441
+ var init_x402_server = __esm({
442
+ "src/init/x402-server.ts"() {
342
443
  "use strict";
343
444
  init_evm();
344
445
  init_solana();
345
446
  init_facilitators();
447
+ init_facilitator_clients();
346
448
  init_accepts();
347
449
  }
348
450
  });
@@ -350,30 +452,19 @@ var init_server = __esm({
350
452
  // src/index.ts
351
453
  var index_exports = {};
352
454
  __export(index_exports, {
353
- BASE_NETWORK: () => BASE_NETWORK,
455
+ BASE_MAINNET_NETWORK: () => BASE_MAINNET_NETWORK,
456
+ BASE_USDC_ADDRESS: () => BASE_USDC_ADDRESS,
457
+ BASE_USDC_DECIMALS: () => BASE_USDC_DECIMALS,
458
+ DEFAULT_SOLANA_FACILITATOR_URL: () => DEFAULT_SOLANA_FACILITATOR_URL,
354
459
  HttpError: () => HttpError,
355
- MemoryEntitlementStore: () => MemoryEntitlementStore,
356
- MemoryNonceStore: () => MemoryNonceStore,
357
- RouteBuilder: () => RouteBuilder,
358
- RouteRegistry: () => RouteRegistry,
359
460
  RouterConfigError: () => RouterConfigError,
360
- SIWX_CHALLENGE_EXPIRY_MS: () => SIWX_CHALLENGE_EXPIRY_MS,
361
- SIWX_ERROR_MESSAGES: () => SIWX_ERROR_MESSAGES,
362
461
  SOLANA_MAINNET_NETWORK: () => SOLANA_MAINNET_NETWORK,
363
- TEMPO_USDC_CURRENCY: () => TEMPO_USDC_CURRENCY,
462
+ TEMPO_USDC_ADDRESS: () => TEMPO_USDC_ADDRESS,
463
+ TEMPO_USDC_DECIMALS: () => TEMPO_USDC_DECIMALS,
364
464
  ZERO_EVM_ADDRESS: () => ZERO_EVM_ADDRESS,
365
- consolePlugin: () => consolePlugin,
366
- createKvEntitlementStore: () => createKvEntitlementStore,
367
- createKvMppStore: () => createKvMppStore,
368
- createKvNonceStore: () => createKvNonceStore,
369
465
  createRouter: () => createRouter,
370
- formatRouterConfigIssues: () => formatRouterConfigIssues,
371
- getRouterConfigIssues: () => getRouterConfigIssues,
372
- mppFromEnv: () => mppFromEnv,
373
- paidOptionsForProtocols: () => paidOptionsForProtocols,
374
- validateRouterConfig: () => validateRouterConfig,
375
- withPrefix: () => withPrefix,
376
- x402AcceptsFromEnv: () => x402AcceptsFromEnv
466
+ createRouterFromEnv: () => createRouterFromEnv,
467
+ routerConfigFromEnv: () => routerConfigFromEnv
377
468
  });
378
469
  module.exports = __toCommonJS(index_exports);
379
470
 
@@ -441,7 +532,7 @@ var AUTH_SCHEME = {
441
532
  MPP_PAYMENT: "Payment "
442
533
  };
443
534
 
444
- // src/plugin.ts
535
+ // src/plugin/index.ts
445
536
  function createDefaultContext(meta) {
446
537
  const ctx = {
447
538
  requestId: meta.requestId,
@@ -477,46 +568,8 @@ function firePluginHook(plugin, method, ...args) {
477
568
  return void 0;
478
569
  }
479
570
  }
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
571
 
519
- // src/alert.ts
572
+ // src/plugin/reporter.ts
520
573
  function createReporter(plugin, pluginCtx, route) {
521
574
  return (level, message, meta) => {
522
575
  firePluginHook(plugin, "onAlert", pluginCtx, {
@@ -528,7 +581,7 @@ function createReporter(plugin, pluginCtx, route) {
528
581
  };
529
582
  }
530
583
 
531
- // src/pipeline/context/preflight.ts
584
+ // src/pipeline/steps/preflight.ts
532
585
  function preflight(routeEntry, handler, deps, request) {
533
586
  const meta = buildMeta(request, routeEntry);
534
587
  const pluginCtx = firePluginHook(deps.plugin, "onRequest", meta) ?? createDefaultContext(meta);
@@ -558,10 +611,10 @@ function buildMeta(request, routeEntry) {
558
611
  };
559
612
  }
560
613
 
561
- // src/pipeline/context/parse-body.ts
614
+ // src/pipeline/steps/parse-body.ts
562
615
  var import_server = require("next/server");
563
616
 
564
- // src/body.ts
617
+ // src/pipeline/body.ts
565
618
  async function bufferBody(request) {
566
619
  const text = await request.text();
567
620
  if (!text) return void 0;
@@ -585,7 +638,19 @@ function validateBody(parsed, schema) {
585
638
  };
586
639
  }
587
640
 
588
- // src/pipeline/context/fire-plugin-response.ts
641
+ // src/plugin/events.ts
642
+ function fireAuthVerified(ctx, event) {
643
+ firePluginHook(ctx.deps.plugin, "onAuthVerified", ctx.pluginCtx, {
644
+ ...event,
645
+ route: ctx.routeEntry.key
646
+ });
647
+ }
648
+ function firePaymentVerified(ctx, event) {
649
+ firePluginHook(ctx.deps.plugin, "onPaymentVerified", ctx.pluginCtx, event);
650
+ }
651
+ function firePaymentSettled(ctx, event) {
652
+ firePluginHook(ctx.deps.plugin, "onPaymentSettled", ctx.pluginCtx, event);
653
+ }
589
654
  function firePluginResponse(ctx, response, requestBody, responseBody) {
590
655
  firePluginHook(ctx.deps.plugin, "onResponse", ctx.pluginCtx, {
591
656
  statusCode: response.status,
@@ -604,8 +669,37 @@ function firePluginResponse(ctx, response, requestBody, responseBody) {
604
669
  });
605
670
  }
606
671
  }
672
+ function fireProviderQuota(ctx, response, handlerResult) {
673
+ const { providerName, providerConfig } = ctx.routeEntry;
674
+ if (!providerName || !providerConfig?.extractQuota) return;
675
+ if (response.status >= 400) return;
676
+ try {
677
+ const quota = providerConfig.extractQuota(handlerResult, response.headers);
678
+ if (!quota) return;
679
+ const level = computeQuotaLevel(quota.remaining, providerConfig.warn, providerConfig.critical);
680
+ const overage = providerConfig.overage ?? "same-rate";
681
+ const event = {
682
+ provider: providerName,
683
+ route: ctx.routeEntry.key,
684
+ remaining: quota.remaining,
685
+ limit: quota.limit,
686
+ spend: quota.spend,
687
+ level,
688
+ overage,
689
+ message: quota.remaining !== null ? `${providerName}: ${quota.remaining}${quota.limit ? `/${quota.limit}` : ""} remaining` : `${providerName}: quota info unavailable`
690
+ };
691
+ firePluginHook(ctx.deps.plugin, "onProviderQuota", ctx.pluginCtx, event);
692
+ } catch {
693
+ }
694
+ }
695
+ function computeQuotaLevel(remaining, warn, critical) {
696
+ if (remaining === null) return "healthy";
697
+ if (critical !== void 0 && remaining <= critical) return "critical";
698
+ if (warn !== void 0 && remaining <= warn) return "warn";
699
+ return "healthy";
700
+ }
607
701
 
608
- // src/pipeline/context/parse-body.ts
702
+ // src/pipeline/steps/parse-body.ts
609
703
  async function parseBody(ctx, request = ctx.request) {
610
704
  if (!ctx.routeEntry.bodySchema) return { ok: true, data: void 0 };
611
705
  const raw = await bufferBody(request);
@@ -619,15 +713,7 @@ async function parseBody(ctx, request = ctx.request) {
619
713
  return { ok: false, response };
620
714
  }
621
715
 
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
716
+ // src/pipeline/steps/errors.ts
631
717
  function errorStatus(error, fallback) {
632
718
  const status = error?.status;
633
719
  return typeof status === "number" ? status : fallback;
@@ -640,7 +726,7 @@ function handlerFailureError(response) {
640
726
  return Object.assign(new Error(message), { status: response.status });
641
727
  }
642
728
 
643
- // src/pipeline/context/fail.ts
729
+ // src/pipeline/steps/fail.ts
644
730
  var import_server2 = require("next/server");
645
731
  function fail(ctx, status, message, requestBody) {
646
732
  const response = import_server2.NextResponse.json({ success: false, error: message }, { status });
@@ -648,7 +734,7 @@ function fail(ctx, status, message, requestBody) {
648
734
  return response;
649
735
  }
650
736
 
651
- // src/pipeline/context/run-validate.ts
737
+ // src/pipeline/steps/run-validate.ts
652
738
  async function runValidate(ctx, body) {
653
739
  if (!ctx.routeEntry.validateFn) return null;
654
740
  try {
@@ -671,6 +757,14 @@ var HttpError = class extends Error {
671
757
  }
672
758
  };
673
759
 
760
+ // src/pipeline/steps/parse-query.ts
761
+ function parseQuery(request, routeEntry) {
762
+ if (!routeEntry.querySchema) return void 0;
763
+ const params = Object.fromEntries(request.nextUrl.searchParams.entries());
764
+ const result = routeEntry.querySchema.safeParse(params);
765
+ return result.success ? result.data : params;
766
+ }
767
+
674
768
  // src/pipeline/flows/static/static-invoke.ts
675
769
  function invokePaidStatic(ctx, wallet, account, body, payment) {
676
770
  return runHandler(ctx, buildHandlerCtx(ctx, wallet, account, body, payment));
@@ -688,14 +782,7 @@ function buildHandlerCtx(ctx, wallet, account, body, payment) {
688
782
  wallet,
689
783
  payment,
690
784
  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
- },
785
+ alert: ctx.report,
699
786
  setVerifiedWallet: (addr) => ctx.pluginCtx.setVerifiedWallet(addr)
700
787
  };
701
788
  }
@@ -739,45 +826,14 @@ function isThenable(value) {
739
826
  return value != null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
740
827
  }
741
828
 
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
829
+ // src/pipeline/steps/finalize/response.ts
774
830
  function finalize(ctx, response, rawResult, requestBody) {
775
831
  fireProviderQuota(ctx, response, rawResult);
776
832
  firePluginResponse(ctx, response, requestBody, rawResult);
777
833
  return response;
778
834
  }
779
835
 
780
- // src/pipeline/context/grant-entitlement.ts
836
+ // src/pipeline/steps/grant-entitlement.ts
781
837
  async function grantEntitlementIfSiwx(ctx, wallet) {
782
838
  if (!ctx.routeEntry.siwxEnabled) return;
783
839
  try {
@@ -790,7 +846,7 @@ async function grantEntitlementIfSiwx(ctx, wallet) {
790
846
  }
791
847
  }
792
848
 
793
- // src/pipeline/context/settlement-context.ts
849
+ // src/pipeline/steps/settlement-context.ts
794
850
  function settlementContext(ctx, scope) {
795
851
  return {
796
852
  route: ctx.routeEntry.key,
@@ -804,7 +860,7 @@ function settlementContext(ctx, scope) {
804
860
  };
805
861
  }
806
862
 
807
- // src/pipeline/context/run-settlement-error.ts
863
+ // src/pipeline/steps/run-settlement-error.ts
808
864
  async function runSettlementError(ctx, scope, error, phase) {
809
865
  const hook = ctx.routeEntry.settlement?.onSettlementError;
810
866
  if (!hook) return;
@@ -816,7 +872,7 @@ async function runSettlementError(ctx, scope, error, phase) {
816
872
  }
817
873
  }
818
874
 
819
- // src/pipeline/context/run-after-settle.ts
875
+ // src/pipeline/steps/run-after-settle.ts
820
876
  async function runAfterSettle(ctx, scope) {
821
877
  const hook = ctx.routeEntry.settlement?.afterSettle;
822
878
  if (!hook) return;
@@ -829,11 +885,11 @@ async function runAfterSettle(ctx, scope) {
829
885
  }
830
886
  }
831
887
 
832
- // src/pipeline/context/finalize/epilogue.ts
888
+ // src/pipeline/steps/finalize/epilogue.ts
833
889
  async function runPostSettleEpilogue(args) {
834
890
  const { ctx, strategy, wallet, settle, afterSettleScope, rawResult, body } = args;
835
891
  await grantEntitlementIfSiwx(ctx, wallet);
836
- firePluginHook(ctx.deps.plugin, "onPaymentSettled", ctx.pluginCtx, {
892
+ firePaymentSettled(ctx, {
837
893
  protocol: strategy.protocol,
838
894
  payer: wallet,
839
895
  transaction: settle.settledPayment.transaction ?? "",
@@ -843,7 +899,7 @@ async function runPostSettleEpilogue(args) {
843
899
  return finalize(ctx, settle.response, rawResult, body);
844
900
  }
845
901
 
846
- // src/pipeline/context/finalize/request.ts
902
+ // src/pipeline/steps/finalize/request.ts
847
903
  async function settleAndFinalizeRequest(args) {
848
904
  const { ctx, strategy, verifyOutcome, scope, rawResult, body, billedAmount, onSettleError } = args;
849
905
  const { request, routeEntry, deps, report } = ctx;
@@ -876,7 +932,7 @@ async function settleAndFinalizeRequest(args) {
876
932
  });
877
933
  }
878
934
 
879
- // src/pipeline/context/finalize/stream.ts
935
+ // src/pipeline/steps/finalize/stream.ts
880
936
  async function settleAndFinalizeStream(args) {
881
937
  const { ctx, strategy, verifyOutcome, source, account, body, bindChannelCharge } = args;
882
938
  const { request, routeEntry, deps, report } = ctx;
@@ -914,7 +970,7 @@ async function settleAndFinalizeStream(args) {
914
970
  });
915
971
  }
916
972
 
917
- // src/pipeline/context/run-handler-only.ts
973
+ // src/pipeline/steps/run-handler-only.ts
918
974
  async function runHandlerOnly(ctx, wallet, account) {
919
975
  const body = await parseBody(ctx);
920
976
  if (!body.ok) return body.response;
@@ -924,7 +980,7 @@ async function runHandlerOnly(ctx, wallet, account) {
924
980
  return finalize(ctx, result.response, result.rawResult, body.data);
925
981
  }
926
982
 
927
- // src/pipeline/context/run-before-settle.ts
983
+ // src/pipeline/steps/run-before-settle.ts
928
984
  async function runBeforeSettle(ctx, scope) {
929
985
  const hook = ctx.routeEntry.settlement?.beforeSettle;
930
986
  if (!hook) return null;
@@ -941,7 +997,7 @@ async function runBeforeSettle(ctx, scope) {
941
997
  }
942
998
  }
943
999
 
944
- // src/pipeline/context/run-settled-handler-error.ts
1000
+ // src/pipeline/steps/run-settled-handler-error.ts
945
1001
  async function runSettledHandlerError(ctx, scope, error = scope.handlerError ?? handlerFailureError(scope.response)) {
946
1002
  const hook = ctx.routeEntry.settlement?.onSettledHandlerError;
947
1003
  if (!hook) return;
@@ -1015,7 +1071,7 @@ async function buildSIWXExtension() {
1015
1071
  return declareSIWxExtension();
1016
1072
  }
1017
1073
 
1018
- // src/pipeline/context/try-siwx-fast-path.ts
1074
+ // src/pipeline/steps/try-siwx-fast-path.ts
1019
1075
  async function trySiwxFastPath(ctx, account) {
1020
1076
  const { request, routeEntry, deps } = ctx;
1021
1077
  if (!routeEntry.siwxEnabled) return null;
@@ -1027,22 +1083,18 @@ async function trySiwxFastPath(ctx, account) {
1027
1083
  ctx.pluginCtx.setVerifiedWallet(wallet);
1028
1084
  const entitled = await deps.entitlementStore.has(routeEntry.key, wallet);
1029
1085
  if (!entitled) return null;
1030
- firePluginHook(deps.plugin, "onAuthVerified", ctx.pluginCtx, {
1031
- authMode: "siwx",
1032
- wallet,
1033
- route: routeEntry.key
1034
- });
1086
+ fireAuthVerified(ctx, { authMode: "siwx", wallet });
1035
1087
  return runHandlerOnly(ctx, wallet, account);
1036
1088
  }
1037
1089
 
1038
- // src/pipeline/context/should-parse-body-early.ts
1090
+ // src/pipeline/steps/should-parse-body-early.ts
1039
1091
  function shouldParseBodyEarly(incomingStrategy, routeEntry, pricing) {
1040
1092
  if (incomingStrategy) return false;
1041
1093
  if (!routeEntry.bodySchema) return false;
1042
1094
  return (pricing?.needsBody ?? false) || !!routeEntry.validateFn;
1043
1095
  }
1044
1096
 
1045
- // src/pipeline/context/resolve-early-body.ts
1097
+ // src/pipeline/steps/resolve-early-body.ts
1046
1098
  async function resolveEarlyBody(args) {
1047
1099
  const { ctx, pricing, incomingStrategy } = args;
1048
1100
  if (!shouldParseBodyEarly(incomingStrategy, ctx.routeEntry, pricing)) {
@@ -1074,24 +1126,19 @@ function extractBearerToken(header) {
1074
1126
  return null;
1075
1127
  }
1076
1128
 
1077
- // src/pipeline/context/run-api-key-gate.ts
1129
+ // src/pipeline/steps/run-api-key-gate.ts
1078
1130
  async function runApiKeyGate(ctx) {
1079
- const { request, routeEntry, deps } = ctx;
1131
+ const { request, routeEntry } = ctx;
1080
1132
  if (!routeEntry.apiKeyResolver) return { ok: true, account: void 0 };
1081
1133
  const apiKeyResult = await verifyApiKey(request, routeEntry.apiKeyResolver);
1082
1134
  if (!apiKeyResult.valid) {
1083
1135
  return { ok: false, response: fail(ctx, 401, "Invalid or missing API key") };
1084
1136
  }
1085
- firePluginHook(deps.plugin, "onAuthVerified", ctx.pluginCtx, {
1086
- authMode: "apiKey",
1087
- wallet: null,
1088
- route: routeEntry.key,
1089
- account: apiKeyResult.account
1090
- });
1137
+ fireAuthVerified(ctx, { authMode: "apiKey", wallet: null, account: apiKeyResult.account });
1091
1138
  return { ok: true, account: apiKeyResult.account };
1092
1139
  }
1093
1140
 
1094
- // src/pipeline/context/protocol-init-error.ts
1141
+ // src/pipeline/steps/protocol-init-error.ts
1095
1142
  function protocolInitError(routeEntry, deps) {
1096
1143
  if (!routeEntry.pricing) return null;
1097
1144
  const errors = [];
@@ -1114,15 +1161,65 @@ async function runApiKeyOnlyFlow(ctx) {
1114
1161
  }
1115
1162
  const result = await verifyApiKey(ctx.request, ctx.routeEntry.apiKeyResolver);
1116
1163
  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
- });
1164
+ fireAuthVerified(ctx, { authMode: "apiKey", wallet: null, account: result.account });
1123
1165
  return runHandlerOnly(ctx, null, result.account);
1124
1166
  }
1125
1167
 
1168
+ // src/pricing/format.ts
1169
+ var USDC_DECIMALS = 6;
1170
+ var DECIMAL_RE = /^(\d+)(?:\.(\d+))?$/;
1171
+ function badDecimal(amount) {
1172
+ return Object.assign(new Error(`'${amount}' is not a valid decimal-dollar string`), {
1173
+ status: 400
1174
+ });
1175
+ }
1176
+ function decimalToAtomic(amount, decimals = USDC_DECIMALS) {
1177
+ const match = DECIMAL_RE.exec(amount.trim());
1178
+ if (!match) throw badDecimal(amount);
1179
+ const whole = match[1];
1180
+ const fraction = match[2] ?? "";
1181
+ if (fraction.length > decimals) {
1182
+ throw Object.assign(new Error(`Amount '${amount}' exceeds ${decimals} decimal places`), {
1183
+ status: 400
1184
+ });
1185
+ }
1186
+ const normalized = `${whole}${fraction.padEnd(decimals, "0")}`.replace(/^0+(?=\d)/, "");
1187
+ return BigInt(normalized || "0");
1188
+ }
1189
+ function atomicToDecimal(atomic, decimals = USDC_DECIMALS) {
1190
+ const divisor = 10n ** BigInt(decimals);
1191
+ const whole = atomic / divisor;
1192
+ const fraction = atomic % divisor;
1193
+ if (fraction === 0n) return whole.toString();
1194
+ const fractionStr = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
1195
+ return `${whole}.${fractionStr}`;
1196
+ }
1197
+ function compareDecimals(a, b) {
1198
+ const av = decimalToAtomic(a);
1199
+ const bv = decimalToAtomic(b);
1200
+ if (av < bv) return -1;
1201
+ if (av > bv) return 1;
1202
+ return 0;
1203
+ }
1204
+ function isPositiveDecimal(value) {
1205
+ try {
1206
+ return decimalToAtomic(value) > 0n;
1207
+ } catch {
1208
+ return false;
1209
+ }
1210
+ }
1211
+ function multiplyDecimal(decimal, factor) {
1212
+ if (!Number.isFinite(factor) || factor <= 0) return decimal;
1213
+ const [whole, fraction = ""] = decimal.split(".");
1214
+ const scaled = (BigInt(whole + fraction) * BigInt(factor)).toString();
1215
+ const decimals = fraction.length;
1216
+ if (decimals === 0) return scaled;
1217
+ const padded = scaled.padStart(decimals + 1, "0");
1218
+ const intPart = padded.slice(0, padded.length - decimals);
1219
+ const fracPart = padded.slice(padded.length - decimals).replace(/0+$/, "");
1220
+ return fracPart ? `${intPart}.${fracPart}` : intPart;
1221
+ }
1222
+
1126
1223
  // src/pricing/dynamic.ts
1127
1224
  var DynamicPricing = class {
1128
1225
  constructor(opts) {
@@ -1158,9 +1255,13 @@ var DynamicPricing = class {
1158
1255
  }
1159
1256
  cap(raw, body) {
1160
1257
  if (!this.opts.maxPrice) return raw;
1161
- const n = parseFloat(raw);
1162
- const max = parseFloat(this.opts.maxPrice);
1163
- if (!Number.isFinite(n) || n > max) {
1258
+ let overCap;
1259
+ try {
1260
+ overCap = compareDecimals(raw, this.opts.maxPrice) > 0;
1261
+ } catch {
1262
+ overCap = true;
1263
+ }
1264
+ if (overCap) {
1164
1265
  this.alert("warn", `Price ${raw} exceeds maxPrice ${this.opts.maxPrice}, capping`, {
1165
1266
  calculated: raw,
1166
1267
  maxPrice: this.opts.maxPrice,
@@ -1237,7 +1338,7 @@ var TieredPricing = class {
1237
1338
  maxTierPrice() {
1238
1339
  let max = "0";
1239
1340
  for (const tier of Object.values(this.opts.tiers)) {
1240
- if (parseFloat(tier.price) > parseFloat(max)) max = tier.price;
1341
+ if (compareDecimals(tier.price, max) > 0) max = tier.price;
1241
1342
  }
1242
1343
  return max;
1243
1344
  }
@@ -1715,17 +1816,6 @@ var mppStrategy = {
1715
1816
  return buildChargeChallenge(args);
1716
1817
  }
1717
1818
  };
1718
- function multiplyDecimal(decimal, factor) {
1719
- if (!Number.isFinite(factor) || factor <= 0) return decimal;
1720
- const [whole, fraction = ""] = decimal.split(".");
1721
- const scaled = (BigInt(whole + fraction) * BigInt(factor)).toString();
1722
- const decimals = fraction.length;
1723
- if (decimals === 0) return scaled;
1724
- const padded = scaled.padStart(decimals + 1, "0");
1725
- const intPart = padded.slice(0, padded.length - decimals);
1726
- const fracPart = padded.slice(padded.length - decimals).replace(/0+$/, "");
1727
- return fracPart ? `${intPart}.${fracPart}` : intPart;
1728
- }
1729
1819
  async function buildChargeChallenge(args) {
1730
1820
  if (!args.deps.mppx) return {};
1731
1821
  try {
@@ -1818,26 +1908,13 @@ function buildCustomRequirement(price, accept) {
1818
1908
  return {
1819
1909
  scheme: accept.scheme,
1820
1910
  network: accept.network,
1821
- amount: decimalToAtomicUnits(price, accept.decimals ?? 6),
1911
+ amount: decimalToAtomic(price, accept.decimals ?? 6).toString(),
1822
1912
  asset: accept.asset,
1823
1913
  payTo: accept.payTo,
1824
1914
  maxTimeoutSeconds: accept.maxTimeoutSeconds ?? 300,
1825
1915
  extra: accept.extra ?? {}
1826
1916
  };
1827
1917
  }
1828
- function decimalToAtomicUnits(amount, decimals) {
1829
- const match = /^(?<whole>\d+)(?:\.(?<fraction>\d+))?$/.exec(amount);
1830
- if (!match?.groups) {
1831
- throw new Error(`Invalid decimal amount '${amount}'`);
1832
- }
1833
- const whole = match.groups.whole;
1834
- const fraction = match.groups.fraction ?? "";
1835
- if (fraction.length > decimals) {
1836
- throw new Error(`Amount '${amount}' exceeds ${decimals} decimal places`);
1837
- }
1838
- const normalized = `${whole}${fraction.padEnd(decimals, "0")}`.replace(/^0+(?=\d)/, "");
1839
- return normalized === "" ? "0" : normalized;
1840
- }
1841
1918
 
1842
1919
  // src/protocols/x402/challenge.ts
1843
1920
  async function buildX402Challenge(opts) {
@@ -2200,28 +2277,14 @@ async function buildX402ChallengeContribution(args) {
2200
2277
  }
2201
2278
  function reportSettleFailure(report, err, network) {
2202
2279
  const facilitator = err ?? {};
2203
- report("error", "Settlement failed", {
2280
+ const meta = {
2204
2281
  error: err instanceof Error ? err.message : String(err),
2205
2282
  network,
2206
2283
  errorReason: facilitator.errorReason,
2207
2284
  facilitatorStatus: facilitator.response?.status,
2208
2285
  facilitatorBody: facilitator.response?.data ?? facilitator.response?.body
2209
- });
2210
- }
2211
-
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;
2286
+ };
2287
+ report("error", "Settlement failed", meta);
2225
2288
  }
2226
2289
 
2227
2290
  // src/protocols/index.ts
@@ -2244,13 +2307,14 @@ function getAllowedStrategies(allowed) {
2244
2307
  var import_server4 = require("next/server");
2245
2308
 
2246
2309
  // src/pipeline/challenge-extensions.ts
2310
+ init_evm();
2247
2311
  async function buildChallengeExtensions(ctx) {
2248
2312
  const { routeEntry } = ctx;
2249
2313
  let extensions;
2250
2314
  try {
2251
- const { z } = await import("zod");
2315
+ const { z: z2 } = await import("zod");
2252
2316
  const { declareDiscoveryExtension } = await import("@x402/extensions/bazaar");
2253
- const toJSON = (schema) => z.toJSONSchema(schema, {
2317
+ const toJSON = (schema) => z2.toJSONSchema(schema, {
2254
2318
  target: "draft-2020-12",
2255
2319
  unrepresentable: "any"
2256
2320
  });
@@ -2271,11 +2335,10 @@ async function buildChallengeExtensions(ctx) {
2271
2335
  extensions = declareDiscoveryExtension(config);
2272
2336
  }
2273
2337
  } 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
- });
2338
+ ctx.report(
2339
+ "warn",
2340
+ `Bazaar schema generation failed: ${err instanceof Error ? err.message : String(err)}`
2341
+ );
2279
2342
  }
2280
2343
  if (routeEntry.siwxEnabled) {
2281
2344
  try {
@@ -2289,7 +2352,24 @@ async function buildChallengeExtensions(ctx) {
2289
2352
  } catch {
2290
2353
  }
2291
2354
  }
2292
- return extensions;
2355
+ const hasEvmUpto = ctx.deps.x402Accepts.some(
2356
+ (accept) => accept.scheme === "upto" && isEvmNetwork(accept.network)
2357
+ );
2358
+ if (hasEvmUpto) {
2359
+ try {
2360
+ const { declareEip2612GasSponsoringExtension } = await import("@x402/extensions");
2361
+ extensions = {
2362
+ ...extensions ?? {},
2363
+ ...declareEip2612GasSponsoringExtension()
2364
+ };
2365
+ } catch (err) {
2366
+ ctx.report(
2367
+ "warn",
2368
+ `EIP-2612 gas-sponsoring declaration failed: ${err instanceof Error ? err.message : String(err)}`
2369
+ );
2370
+ }
2371
+ }
2372
+ return extensions;
2293
2373
  }
2294
2374
 
2295
2375
  // src/pipeline/flows/build402.ts
@@ -2409,7 +2489,7 @@ async function runDynamicChannelMgmtFlow(args) {
2409
2489
  return build402(ctx, pricing, parsedBody, verifyOutcome.failure);
2410
2490
  }
2411
2491
  ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
2412
- firePluginHook(deps.plugin, "onPaymentVerified", ctx.pluginCtx, {
2492
+ firePaymentVerified(ctx, {
2413
2493
  protocol: strategy.protocol,
2414
2494
  payer: verifyOutcome.wallet,
2415
2495
  amount: price,
@@ -2444,27 +2524,6 @@ async function runDynamicChannelMgmtFlow(args) {
2444
2524
  // src/pipeline/flows/dynamic/dynamic-invoke.ts
2445
2525
  var import_server6 = require("next/server");
2446
2526
 
2447
- // src/pricing/atomic.ts
2448
- var USDC_DECIMALS = 6;
2449
- function decimalToAtomic(amount) {
2450
- const m = /^(\d+)(?:\.(\d+))?$/.exec(amount.trim());
2451
- if (!m) {
2452
- throw Object.assign(new Error(`'${amount}' is not a valid decimal-dollar string`), {
2453
- status: 400
2454
- });
2455
- }
2456
- const whole = m[1];
2457
- const fraction = (m[2] ?? "").slice(0, USDC_DECIMALS).padEnd(USDC_DECIMALS, "0");
2458
- return BigInt(`${whole}${fraction}`.replace(/^0+(?=\d)/, "") || "0");
2459
- }
2460
- function atomicToDecimal(atomic) {
2461
- const whole = atomic / 10n ** BigInt(USDC_DECIMALS);
2462
- const fraction = atomic % 10n ** BigInt(USDC_DECIMALS);
2463
- if (fraction === 0n) return whole.toString();
2464
- const fractionStr = fraction.toString().padStart(USDC_DECIMALS, "0").replace(/0+$/, "");
2465
- return `${whole}.${fractionStr}`;
2466
- }
2467
-
2468
2527
  // src/pricing/charge-context.ts
2469
2528
  function createChargeContext(args) {
2470
2529
  const { tickCost, maxPrice, route } = args;
@@ -2517,14 +2576,7 @@ async function invokeDynamic(ctx, wallet, account, body, payment) {
2517
2576
  wallet,
2518
2577
  payment,
2519
2578
  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
- },
2579
+ alert: ctx.report,
2528
2580
  setVerifiedWallet: (addr) => ctx.pluginCtx.setVerifiedWallet(addr)
2529
2581
  };
2530
2582
  const handlerCtx = chargeContext !== null ? { ...baseHandlerCtx, charge: chargeContext.charge } : baseHandlerCtx;
@@ -2589,7 +2641,7 @@ function resolveDynamicPreflight(strategy, request, routeEntry) {
2589
2641
  // src/pipeline/flows/dynamic/dynamic-request.ts
2590
2642
  async function runDynamicRequestFlow(args) {
2591
2643
  const { ctx, strategy, verifyOutcome, account, body, result } = args;
2592
- const { deps, routeEntry } = ctx;
2644
+ const { routeEntry } = ctx;
2593
2645
  const settleScope = {
2594
2646
  wallet: verifyOutcome.wallet,
2595
2647
  account,
@@ -2615,11 +2667,10 @@ async function runDynamicRequestFlow(args) {
2615
2667
  billedAmount,
2616
2668
  onSettleError: async (error, failMessage) => {
2617
2669
  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
- });
2670
+ ctx.report(
2671
+ "critical",
2672
+ `${strategy.protocol} ${failMessage}: ${errorMessage(error, "unknown")}`
2673
+ );
2623
2674
  }
2624
2675
  });
2625
2676
  }
@@ -2689,7 +2740,7 @@ async function runDynamicPaidFlow(ctx) {
2689
2740
  return build402(ctx, pricing, parsedBody, verifyOutcome.failure);
2690
2741
  }
2691
2742
  ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
2692
- firePluginHook(deps.plugin, "onPaymentVerified", ctx.pluginCtx, {
2743
+ firePaymentVerified(ctx, {
2693
2744
  protocol: incomingStrategy.protocol,
2694
2745
  payer: verifyOutcome.wallet,
2695
2746
  amount: price,
@@ -2755,7 +2806,6 @@ async function resolveStaticBodyAndPrice(args) {
2755
2806
  // src/pipeline/flows/static/static-request.ts
2756
2807
  async function runStaticRequestFlow(args) {
2757
2808
  const { ctx, strategy, verifyOutcome, account, body, price, result } = args;
2758
- const { deps, routeEntry } = ctx;
2759
2809
  const settleScope = {
2760
2810
  wallet: verifyOutcome.wallet,
2761
2811
  account,
@@ -2796,11 +2846,10 @@ async function runStaticRequestFlow(args) {
2796
2846
  billedAmount: price,
2797
2847
  onSettleError: async (error, failMessage) => {
2798
2848
  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
- });
2849
+ ctx.report(
2850
+ "critical",
2851
+ `${strategy.protocol} ${failMessage}: ${errorMessage(error, "unknown")}`
2852
+ );
2804
2853
  }
2805
2854
  });
2806
2855
  }
@@ -2846,7 +2895,7 @@ async function runStaticPaidFlow(ctx) {
2846
2895
  return build402(ctx, pricing, parsedBody, verifyOutcome.failure);
2847
2896
  }
2848
2897
  ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
2849
- firePluginHook(deps.plugin, "onPaymentVerified", ctx.pluginCtx, {
2898
+ firePaymentVerified(ctx, {
2850
2899
  protocol: incomingStrategy.protocol,
2851
2900
  payer: verifyOutcome.wallet,
2852
2901
  amount: price,
@@ -2885,6 +2934,19 @@ async function runPaidFlow(ctx) {
2885
2934
  var import_server7 = require("next/server");
2886
2935
 
2887
2936
  // src/kv-store/client.ts
2937
+ var BIGINT_SUFFIX = "#__bigint";
2938
+ function stringifyValue(value) {
2939
+ return JSON.stringify(
2940
+ value,
2941
+ (_key, v) => typeof v === "bigint" ? `${v.toString()}${BIGINT_SUFFIX}` : v
2942
+ );
2943
+ }
2944
+ function parseValue(raw) {
2945
+ return JSON.parse(
2946
+ raw,
2947
+ (_key, v) => typeof v === "string" && v.endsWith(BIGINT_SUFFIX) ? BigInt(v.slice(0, -BIGINT_SUFFIX.length)) : v
2948
+ );
2949
+ }
2888
2950
  function restKvStore(url, token) {
2889
2951
  const base = url.replace(/\/+$/, "");
2890
2952
  const authHeader = { Authorization: `Bearer ${token}` };
@@ -2906,16 +2968,22 @@ function restKvStore(url, token) {
2906
2968
  const res = await fetch(`${base}/get/${encodeURIComponent(key)}`, { headers: authHeader });
2907
2969
  if (!res.ok) throw new Error(`[kv-store] GET ${key}: ${res.status}`);
2908
2970
  const { result } = await res.json();
2909
- return result ?? null;
2971
+ if (result == null) return null;
2972
+ if (typeof result !== "string") return result;
2973
+ try {
2974
+ return parseValue(result);
2975
+ } catch {
2976
+ return result;
2977
+ }
2910
2978
  }
2911
2979
  async function set(key, value) {
2912
- await exec(["SET", key, JSON.stringify(value)]);
2980
+ await exec(["SET", key, stringifyValue(value)]);
2913
2981
  }
2914
2982
  async function del(key) {
2915
2983
  await exec(["DEL", key]);
2916
2984
  }
2917
2985
  async function setNxEx(key, value, ttlSeconds) {
2918
- const result = await exec(["SET", key, JSON.stringify(value), "EX", ttlSeconds, "NX"]);
2986
+ const result = await exec(["SET", key, stringifyValue(value), "EX", ttlSeconds, "NX"]);
2919
2987
  return result === "OK";
2920
2988
  }
2921
2989
  async function sadd(key, member) {
@@ -3030,6 +3098,21 @@ async function createKvMppStore(kv, options) {
3030
3098
  });
3031
3099
  }
3032
3100
 
3101
+ // src/protocols/detect.ts
3102
+ function detectProtocol(request) {
3103
+ if (request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)) {
3104
+ return "x402";
3105
+ }
3106
+ const auth = request.headers.get(HEADERS.AUTHORIZATION);
3107
+ if (auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT)) {
3108
+ return "mpp";
3109
+ }
3110
+ if (request.headers.get(HEADERS.SIWX)) {
3111
+ return "siwx";
3112
+ }
3113
+ return null;
3114
+ }
3115
+
3033
3116
  // src/protocols/mpp/siwx-mode.ts
3034
3117
  var import_mppx3 = require("mppx");
3035
3118
  async function verifyMppSiwx(request, mppx) {
@@ -3069,11 +3152,7 @@ async function runSiwxOnlyFlow(ctx) {
3069
3152
  }
3070
3153
  if (mppSiwxResult.valid) {
3071
3154
  ctx.pluginCtx.setVerifiedWallet(mppSiwxResult.wallet);
3072
- firePluginHook(deps.plugin, "onAuthVerified", ctx.pluginCtx, {
3073
- authMode: "siwx",
3074
- wallet: mppSiwxResult.wallet,
3075
- route: routeEntry.key
3076
- });
3155
+ fireAuthVerified(ctx, { authMode: "siwx", wallet: mppSiwxResult.wallet });
3077
3156
  const authResponse = await runHandlerOnly(ctx, mppSiwxResult.wallet, void 0);
3078
3157
  if (authResponse.status < 400) {
3079
3158
  return mppSiwxResult.withReceipt(authResponse);
@@ -3095,11 +3174,7 @@ async function runSiwxOnlyFlow(ctx) {
3095
3174
  }
3096
3175
  const wallet = normalizeWalletAddress(siwx.wallet);
3097
3176
  ctx.pluginCtx.setVerifiedWallet(wallet);
3098
- firePluginHook(deps.plugin, "onAuthVerified", ctx.pluginCtx, {
3099
- authMode: "siwx",
3100
- wallet,
3101
- route: routeEntry.key
3102
- });
3177
+ fireAuthVerified(ctx, { authMode: "siwx", wallet });
3103
3178
  return runHandlerOnly(ctx, wallet, void 0);
3104
3179
  }
3105
3180
  async function buildSiwxChallenge(ctx) {
@@ -3192,7 +3267,7 @@ async function runUnprotectedFlow(ctx) {
3192
3267
  return runHandlerOnly(ctx, null, void 0);
3193
3268
  }
3194
3269
 
3195
- // src/orchestrate.ts
3270
+ // src/pipeline/orchestrate.ts
3196
3271
  function createRequestHandler(routeEntry, handler, deps) {
3197
3272
  return async (request) => {
3198
3273
  await deps.initPromise;
@@ -3237,142 +3312,109 @@ ${issues}`
3237
3312
  }
3238
3313
 
3239
3314
  // src/builder.ts
3240
- var RouteBuilder = class {
3241
- /** @internal */
3242
- _key;
3243
- /** @internal */
3244
- _registry;
3245
- /** @internal */
3246
- _deps;
3247
- /** @internal */
3248
- _authMode = null;
3249
- /** @internal */
3250
- _pricing;
3251
- /** @internal */
3252
- _siwxEnabled = false;
3253
- /** @internal */
3254
- _protocols = ["x402"];
3255
- /** @internal */
3256
- _maxPrice;
3257
- /** @internal */
3258
- _minPrice;
3259
- /** @internal */
3260
- _dynamicPrice = false;
3261
- /** @internal */
3262
- _tickCost;
3263
- /** @internal */
3264
- _unitType;
3265
- /** @internal */
3266
- _payTo;
3267
- /** @internal */
3268
- _bodySchema;
3269
- /** @internal */
3270
- _querySchema;
3271
- /** @internal */
3272
- _outputSchema;
3273
- /** @internal */
3274
- _inputExample = void 0;
3275
- /** @internal */
3276
- _hasInputExample = false;
3277
- /** @internal */
3278
- _outputExample = void 0;
3279
- /** @internal */
3280
- _hasOutputExample = false;
3281
- /** @internal */
3282
- _description;
3283
- /** @internal */
3284
- _path;
3285
- /** @internal */
3286
- _method = "POST";
3287
- /** @internal */
3288
- _apiKeyResolver;
3289
- /** @internal */
3290
- _providerName;
3291
- /** @internal */
3292
- _providerConfig;
3293
- /** @internal */
3294
- _validateFn;
3295
- /** @internal */
3296
- _settlement;
3297
- /** @internal */
3298
- _mppInfo;
3299
- constructor(key, registry, deps) {
3300
- this._key = key;
3301
- this._registry = registry;
3302
- this._deps = deps;
3315
+ var RouteBuilder = class _RouteBuilder {
3316
+ #s;
3317
+ constructor(key, registry, deps, defaults) {
3318
+ this.#s = {
3319
+ key,
3320
+ registry,
3321
+ deps,
3322
+ authMode: null,
3323
+ pricing: void 0,
3324
+ siwxEnabled: false,
3325
+ protocols: defaults?.protocols ? [...defaults.protocols] : ["x402"],
3326
+ maxPrice: void 0,
3327
+ minPrice: void 0,
3328
+ dynamicPrice: false,
3329
+ tickCost: void 0,
3330
+ unitType: void 0,
3331
+ payTo: void 0,
3332
+ bodySchema: void 0,
3333
+ querySchema: void 0,
3334
+ outputSchema: void 0,
3335
+ inputExample: void 0,
3336
+ hasInputExample: false,
3337
+ outputExample: void 0,
3338
+ hasOutputExample: false,
3339
+ description: void 0,
3340
+ path: void 0,
3341
+ method: "POST",
3342
+ apiKeyResolver: void 0,
3343
+ providerName: void 0,
3344
+ providerConfig: void 0,
3345
+ validateFn: void 0,
3346
+ settlement: void 0,
3347
+ mppInfo: void 0
3348
+ };
3303
3349
  }
3304
3350
  fork() {
3305
- const next = Object.create(Object.getPrototypeOf(this));
3306
- Object.assign(next, this);
3307
- next._protocols = [...this._protocols];
3351
+ const next = new _RouteBuilder(
3352
+ this.#s.key,
3353
+ this.#s.registry,
3354
+ this.#s.deps
3355
+ );
3356
+ next.#s = { ...this.#s, protocols: [...this.#s.protocols] };
3308
3357
  return next;
3309
3358
  }
3310
3359
  paid(pricingOrOptions, options) {
3311
- const { pricing, resolvedOptions } = resolvePaidArgs(this._key, pricingOrOptions, options);
3312
- if (this._authMode === "unprotected") {
3360
+ const { pricing, resolvedOptions } = resolvePaidArgs(this.#s.key, pricingOrOptions, options);
3361
+ if (this.#s.authMode === "unprotected") {
3313
3362
  throw new Error(
3314
- `route '${this._key}': Cannot combine .unprotected() and .paid() on the same route.`
3363
+ `route '${this.#s.key}': Cannot combine .unprotected() and .paid() on the same route.`
3315
3364
  );
3316
3365
  }
3317
- if (this._pricing !== void 0) {
3366
+ if (this.#s.pricing !== void 0) {
3318
3367
  throw new Error(
3319
- `route '${this._key}': Cannot call .paid() more than once on the same route.`
3368
+ `route '${this.#s.key}': Cannot call .paid() more than once on the same route.`
3320
3369
  );
3321
3370
  }
3322
3371
  const next = this.fork();
3323
- next._authMode = "paid";
3324
- next._pricing = pricing;
3372
+ next.#s.authMode = "paid";
3373
+ next.#s.pricing = pricing;
3325
3374
  if (resolvedOptions?.protocols) {
3326
- next._protocols = [...resolvedOptions.protocols];
3327
- } else if (next._protocols.length === 0) {
3328
- next._protocols = ["x402"];
3375
+ next.#s.protocols = [...resolvedOptions.protocols];
3376
+ } else if (next.#s.protocols.length === 0) {
3377
+ next.#s.protocols = ["x402"];
3329
3378
  }
3330
- if (resolvedOptions?.maxPrice) next._maxPrice = resolvedOptions.maxPrice;
3331
- if (resolvedOptions?.minPrice) next._minPrice = resolvedOptions.minPrice;
3332
- if (resolvedOptions?.payTo) next._payTo = resolvedOptions.payTo;
3333
- if (resolvedOptions?.mpp) next._mppInfo = resolvedOptions.mpp;
3334
- if (resolvedOptions?.dynamic) next._dynamicPrice = true;
3335
- if (resolvedOptions?.tickCost) next._tickCost = resolvedOptions.tickCost;
3336
- if (resolvedOptions?.unitType) next._unitType = resolvedOptions.unitType;
3379
+ if (resolvedOptions?.maxPrice) next.#s.maxPrice = resolvedOptions.maxPrice;
3380
+ if (resolvedOptions?.minPrice) next.#s.minPrice = resolvedOptions.minPrice;
3381
+ if (resolvedOptions?.payTo) next.#s.payTo = resolvedOptions.payTo;
3382
+ if (resolvedOptions?.mpp) next.#s.mppInfo = resolvedOptions.mpp;
3383
+ if (resolvedOptions?.dynamic) next.#s.dynamicPrice = true;
3384
+ if (resolvedOptions?.tickCost) next.#s.tickCost = resolvedOptions.tickCost;
3385
+ if (resolvedOptions?.unitType) next.#s.unitType = resolvedOptions.unitType;
3337
3386
  if (typeof pricing === "object" && "tiers" in pricing) {
3338
- if (next._dynamicPrice) {
3387
+ if (next.#s.dynamicPrice) {
3339
3388
  throw new Error(
3340
- `route '${this._key}': .paid({ dynamic: true }) is incompatible with tiered pricing`
3389
+ `route '${this.#s.key}': .paid({ dynamic: true }) is incompatible with tiered pricing`
3341
3390
  );
3342
3391
  }
3343
3392
  for (const [tierKey, tierConfig] of Object.entries(pricing.tiers)) {
3344
3393
  if (!tierKey) {
3345
- throw new Error(`route '${this._key}': tier key cannot be empty`);
3394
+ throw new Error(`route '${this.#s.key}': tier key cannot be empty`);
3346
3395
  }
3347
- const tierPrice = parseFloat(tierConfig.price);
3348
- if (isNaN(tierPrice) || tierPrice <= 0) {
3396
+ if (!isPositiveDecimal(tierConfig.price)) {
3349
3397
  throw new Error(
3350
- `route '${this._key}': tier '${tierKey}' price '${tierConfig.price}' must be a positive decimal string`
3398
+ `route '${this.#s.key}': tier '${tierKey}' price '${tierConfig.price}' must be a positive decimal string`
3351
3399
  );
3352
3400
  }
3353
3401
  }
3354
3402
  }
3355
- if (resolvedOptions?.maxPrice !== void 0) {
3356
- const parsed = parseFloat(resolvedOptions.maxPrice);
3357
- if (isNaN(parsed) || parsed <= 0) {
3358
- throw new Error(
3359
- `route '${this._key}': maxPrice '${resolvedOptions.maxPrice}' must be a positive decimal string`
3360
- );
3361
- }
3403
+ if (resolvedOptions?.maxPrice !== void 0 && !isPositiveDecimal(resolvedOptions.maxPrice)) {
3404
+ throw new Error(
3405
+ `route '${this.#s.key}': maxPrice '${resolvedOptions.maxPrice}' must be a positive decimal string`
3406
+ );
3362
3407
  }
3363
- if (resolvedOptions?.tickCost !== void 0) {
3364
- const parsed = parseFloat(resolvedOptions.tickCost);
3365
- if (isNaN(parsed) || parsed <= 0) {
3366
- throw new Error(
3367
- `route '${this._key}': tickCost '${resolvedOptions.tickCost}' must be a positive decimal string`
3368
- );
3369
- }
3408
+ if (resolvedOptions?.tickCost !== void 0 && !isPositiveDecimal(resolvedOptions.tickCost)) {
3409
+ throw new Error(
3410
+ `route '${this.#s.key}': tickCost '${resolvedOptions.tickCost}' must be a positive decimal string`
3411
+ );
3370
3412
  }
3371
- if (next._dynamicPrice && !next._maxPrice) {
3372
- throw new Error(`route '${this._key}': .paid({ dynamic: true }) requires maxPrice`);
3413
+ if (next.#s.dynamicPrice && !next.#s.maxPrice) {
3414
+ throw new Error(`route '${this.#s.key}': .paid({ dynamic: true }) requires maxPrice`);
3373
3415
  }
3374
- if (next._dynamicPrice && !next._tickCost) {
3375
- throw new Error(`route '${this._key}': .paid({ dynamic: true }) requires tickCost`);
3416
+ if (next.#s.dynamicPrice && !next.#s.tickCost) {
3417
+ throw new Error(`route '${this.#s.key}': .paid({ dynamic: true }) requires tickCost`);
3376
3418
  }
3377
3419
  return next;
3378
3420
  }
@@ -3387,25 +3429,25 @@ var RouteBuilder = class {
3387
3429
  * ```
3388
3430
  */
3389
3431
  siwx() {
3390
- if (this._authMode === "unprotected") {
3432
+ if (this.#s.authMode === "unprotected") {
3391
3433
  throw new Error(
3392
- `route '${this._key}': Cannot combine .unprotected() and .siwx() on the same route.`
3434
+ `route '${this.#s.key}': Cannot combine .unprotected() and .siwx() on the same route.`
3393
3435
  );
3394
3436
  }
3395
- if (this._apiKeyResolver) {
3437
+ if (this.#s.apiKeyResolver) {
3396
3438
  throw new Error(
3397
- `route '${this._key}': Combining .siwx() and .apiKey() is not supported on the same route.`
3439
+ `route '${this.#s.key}': Combining .siwx() and .apiKey() is not supported on the same route.`
3398
3440
  );
3399
3441
  }
3400
3442
  const next = this.fork();
3401
- next._siwxEnabled = true;
3402
- if (next._authMode === "paid" || next._pricing) {
3403
- next._authMode = "paid";
3404
- if (next._protocols.length === 0) next._protocols = ["x402"];
3443
+ next.#s.siwxEnabled = true;
3444
+ if (next.#s.authMode === "paid" || next.#s.pricing) {
3445
+ next.#s.authMode = "paid";
3446
+ if (next.#s.protocols.length === 0) next.#s.protocols = ["x402"];
3405
3447
  return next;
3406
3448
  }
3407
- next._authMode = "siwx";
3408
- next._protocols = [];
3449
+ next.#s.authMode = "siwx";
3450
+ next.#s.protocols = [];
3409
3451
  return next;
3410
3452
  }
3411
3453
  /**
@@ -3422,14 +3464,14 @@ var RouteBuilder = class {
3422
3464
  * ```
3423
3465
  */
3424
3466
  apiKey(resolver) {
3425
- if (this._siwxEnabled) {
3467
+ if (this.#s.siwxEnabled) {
3426
3468
  throw new Error(
3427
- `route '${this._key}': Combining .apiKey() and .siwx() is not supported on the same route.`
3469
+ `route '${this.#s.key}': Combining .apiKey() and .siwx() is not supported on the same route.`
3428
3470
  );
3429
3471
  }
3430
3472
  const next = this.fork();
3431
- next._authMode = "apiKey";
3432
- next._apiKeyResolver = resolver;
3473
+ next.#s.authMode = "apiKey";
3474
+ next.#s.apiKeyResolver = resolver;
3433
3475
  return next;
3434
3476
  }
3435
3477
  /**
@@ -3442,19 +3484,19 @@ var RouteBuilder = class {
3442
3484
  * ```
3443
3485
  */
3444
3486
  unprotected() {
3445
- if (this._authMode && this._authMode !== "unprotected") {
3487
+ if (this.#s.authMode && this.#s.authMode !== "unprotected") {
3446
3488
  throw new Error(
3447
- `route '${this._key}': Cannot combine .unprotected() and .${this._authMode}() on the same route.`
3489
+ `route '${this.#s.key}': Cannot combine .unprotected() and .${this.#s.authMode}() on the same route.`
3448
3490
  );
3449
3491
  }
3450
- if (this._pricing) {
3492
+ if (this.#s.pricing) {
3451
3493
  throw new Error(
3452
- `route '${this._key}': Cannot combine .unprotected() and .paid() on the same route.`
3494
+ `route '${this.#s.key}': Cannot combine .unprotected() and .paid() on the same route.`
3453
3495
  );
3454
3496
  }
3455
3497
  const next = this.fork();
3456
- next._authMode = "unprotected";
3457
- next._protocols = [];
3498
+ next.#s.authMode = "unprotected";
3499
+ next.#s.protocols = [];
3458
3500
  return next;
3459
3501
  }
3460
3502
  /**
@@ -3473,8 +3515,8 @@ var RouteBuilder = class {
3473
3515
  */
3474
3516
  provider(name, config) {
3475
3517
  const next = this.fork();
3476
- next._providerName = name;
3477
- next._providerConfig = config ?? {};
3518
+ next.#s.providerName = name;
3519
+ next.#s.providerConfig = config ?? {};
3478
3520
  return next;
3479
3521
  }
3480
3522
  /**
@@ -3489,7 +3531,7 @@ var RouteBuilder = class {
3489
3531
  */
3490
3532
  body(schema) {
3491
3533
  const next = this.fork();
3492
- next._bodySchema = schema;
3534
+ next.#s.bodySchema = schema;
3493
3535
  return next;
3494
3536
  }
3495
3537
  /**
@@ -3505,8 +3547,8 @@ var RouteBuilder = class {
3505
3547
  */
3506
3548
  query(schema) {
3507
3549
  const next = this.fork();
3508
- next._querySchema = schema;
3509
- next._method = "GET";
3550
+ next.#s.querySchema = schema;
3551
+ next.#s.method = "GET";
3510
3552
  return next;
3511
3553
  }
3512
3554
  /**
@@ -3523,7 +3565,7 @@ var RouteBuilder = class {
3523
3565
  */
3524
3566
  output(schema) {
3525
3567
  const next = this.fork();
3526
- next._outputSchema = schema;
3568
+ next.#s.outputSchema = schema;
3527
3569
  return next;
3528
3570
  }
3529
3571
  /**
@@ -3537,8 +3579,8 @@ var RouteBuilder = class {
3537
3579
  */
3538
3580
  inputExample(example) {
3539
3581
  const next = this.fork();
3540
- next._inputExample = example;
3541
- next._hasInputExample = true;
3582
+ next.#s.inputExample = example;
3583
+ next.#s.hasInputExample = true;
3542
3584
  return next;
3543
3585
  }
3544
3586
  /**
@@ -3552,8 +3594,8 @@ var RouteBuilder = class {
3552
3594
  */
3553
3595
  outputExample(example) {
3554
3596
  const next = this.fork();
3555
- next._outputExample = example;
3556
- next._hasOutputExample = true;
3597
+ next.#s.outputExample = example;
3598
+ next.#s.hasOutputExample = true;
3557
3599
  return next;
3558
3600
  }
3559
3601
  /**
@@ -3567,7 +3609,7 @@ var RouteBuilder = class {
3567
3609
  */
3568
3610
  description(text) {
3569
3611
  const next = this.fork();
3570
- next._description = text;
3612
+ next.#s.description = text;
3571
3613
  return next;
3572
3614
  }
3573
3615
  /**
@@ -3581,7 +3623,7 @@ var RouteBuilder = class {
3581
3623
  */
3582
3624
  path(p) {
3583
3625
  const next = this.fork();
3584
- next._path = p;
3626
+ next.#s.path = p;
3585
3627
  return next;
3586
3628
  }
3587
3629
  /**
@@ -3595,7 +3637,7 @@ var RouteBuilder = class {
3595
3637
  */
3596
3638
  method(m) {
3597
3639
  const next = this.fork();
3598
- next._method = m;
3640
+ next.#s.method = m;
3599
3641
  return next;
3600
3642
  }
3601
3643
  /**
@@ -3614,7 +3656,7 @@ var RouteBuilder = class {
3614
3656
  */
3615
3657
  validate(fn) {
3616
3658
  const next = this.fork();
3617
- next._validateFn = fn;
3659
+ next.#s.validateFn = fn;
3618
3660
  return next;
3619
3661
  }
3620
3662
  /**
@@ -3632,7 +3674,7 @@ var RouteBuilder = class {
3632
3674
  */
3633
3675
  settlement(lifecycle) {
3634
3676
  const next = this.fork();
3635
- next._settlement = lifecycle;
3677
+ next.#s.settlement = lifecycle;
3636
3678
  return next;
3637
3679
  }
3638
3680
  /**
@@ -3675,79 +3717,79 @@ var RouteBuilder = class {
3675
3717
  return this.register(fn, true);
3676
3718
  }
3677
3719
  register(handlerFn, streaming) {
3678
- if (!this._authMode) {
3720
+ if (!this.#s.authMode) {
3679
3721
  throw new Error(
3680
- `route '${this._key}': Select an auth mode: .paid(pricing), .siwx(), .apiKey(resolver), or .unprotected()`
3722
+ `route '${this.#s.key}': Select an auth mode: .paid(pricing), .siwx(), .apiKey(resolver), or .unprotected()`
3681
3723
  );
3682
3724
  }
3683
- if (this._validateFn && !this._bodySchema) {
3725
+ if (this.#s.validateFn && !this.#s.bodySchema) {
3684
3726
  throw new Error(
3685
- `route '${this._key}': .validate() requires .body() \u2014 validation runs on parsed body`
3727
+ `route '${this.#s.key}': .validate() requires .body() \u2014 validation runs on parsed body`
3686
3728
  );
3687
3729
  }
3688
- if (this._settlement && !this._pricing) {
3689
- throw new Error(`route '${this._key}': .settlement() requires a paid route`);
3730
+ if (this.#s.settlement && !this.#s.pricing) {
3731
+ throw new Error(`route '${this.#s.key}': .settlement() requires a paid route`);
3690
3732
  }
3691
- if (this._dynamicPrice && this._protocols.includes("x402")) {
3692
- const hasUpto = this._deps.x402Accepts.some((accept) => accept.scheme === "upto");
3733
+ if (this.#s.dynamicPrice && this.#s.protocols.includes("x402")) {
3734
+ const hasUpto = this.#s.deps.x402Accepts.some((accept) => accept.scheme === "upto");
3693
3735
  if (!hasUpto) {
3694
3736
  throw new Error(
3695
- `route '${this._key}': .paid({ dynamic: true }) on an x402 route requires an 'upto' accept on at least one configured network. Add { scheme: 'upto', network, asset } to RouterConfig.x402.accepts.`
3737
+ `route '${this.#s.key}': .paid({ dynamic: true }) on an x402 route requires an 'upto' accept on at least one configured network. Add { scheme: 'upto', network, asset } to RouterConfig.x402.accepts.`
3696
3738
  );
3697
3739
  }
3698
3740
  }
3699
- if (this._dynamicPrice && this._protocols.includes("mpp")) {
3700
- if (!this._deps.mppSessionConfig) {
3741
+ if (this.#s.dynamicPrice && this.#s.protocols.includes("mpp")) {
3742
+ if (!this.#s.deps.mppSessionConfig) {
3701
3743
  throw new Error(
3702
- `route '${this._key}': .paid({ dynamic: true }) on an MPP route requires session mode. Set RouterConfig.mpp.session = {} and provide mpp.operatorKey.`
3744
+ `route '${this.#s.key}': .paid({ dynamic: true }) on an MPP route requires session mode. Set RouterConfig.mpp.session = {} and provide mpp.operatorKey.`
3703
3745
  );
3704
3746
  }
3705
3747
  }
3706
- if (streaming && !this._dynamicPrice) {
3748
+ if (streaming && !this.#s.dynamicPrice) {
3707
3749
  throw new Error(
3708
- `route '${this._key}': .stream() requires .paid({ dynamic: true }) \u2014 static/free routes can't meter per-chunk billing.`
3750
+ `route '${this.#s.key}': .stream() requires .paid({ dynamic: true }) \u2014 static/free routes can't meter per-chunk billing.`
3709
3751
  );
3710
3752
  }
3711
3753
  validateExamples(
3712
- this._key,
3713
- this._bodySchema,
3714
- this._querySchema,
3715
- this._outputSchema,
3716
- this._inputExample,
3717
- this._hasInputExample,
3718
- this._outputExample,
3719
- this._hasOutputExample
3754
+ this.#s.key,
3755
+ this.#s.bodySchema,
3756
+ this.#s.querySchema,
3757
+ this.#s.outputSchema,
3758
+ this.#s.inputExample,
3759
+ this.#s.hasInputExample,
3760
+ this.#s.outputExample,
3761
+ this.#s.hasOutputExample
3720
3762
  );
3721
3763
  const entry = {
3722
- key: this._key,
3723
- authMode: this._authMode,
3724
- siwxEnabled: this._siwxEnabled,
3725
- pricing: this._pricing,
3726
- dynamicPrice: this._dynamicPrice ? true : void 0,
3764
+ key: this.#s.key,
3765
+ authMode: this.#s.authMode,
3766
+ siwxEnabled: this.#s.siwxEnabled,
3767
+ pricing: this.#s.pricing,
3768
+ dynamicPrice: this.#s.dynamicPrice ? true : void 0,
3727
3769
  streaming: streaming ? true : void 0,
3728
- protocols: this._protocols,
3729
- bodySchema: this._bodySchema,
3730
- querySchema: this._querySchema,
3731
- outputSchema: this._outputSchema,
3732
- inputExample: this._hasInputExample ? this._inputExample : void 0,
3733
- outputExample: this._hasOutputExample ? this._outputExample : void 0,
3734
- description: this._description,
3735
- path: this._path,
3736
- method: this._method,
3737
- maxPrice: this._maxPrice,
3738
- minPrice: this._minPrice,
3739
- payTo: this._payTo,
3740
- apiKeyResolver: this._apiKeyResolver,
3741
- providerName: this._providerName,
3742
- providerConfig: this._providerConfig,
3743
- validateFn: this._validateFn,
3744
- settlement: this._settlement,
3745
- mppInfo: this._mppInfo,
3746
- tickCost: this._tickCost,
3747
- unitType: this._unitType
3770
+ protocols: this.#s.protocols,
3771
+ bodySchema: this.#s.bodySchema,
3772
+ querySchema: this.#s.querySchema,
3773
+ outputSchema: this.#s.outputSchema,
3774
+ inputExample: this.#s.hasInputExample ? this.#s.inputExample : void 0,
3775
+ outputExample: this.#s.hasOutputExample ? this.#s.outputExample : void 0,
3776
+ description: this.#s.description,
3777
+ path: this.#s.path,
3778
+ method: this.#s.method,
3779
+ maxPrice: this.#s.maxPrice,
3780
+ minPrice: this.#s.minPrice,
3781
+ payTo: this.#s.payTo,
3782
+ apiKeyResolver: this.#s.apiKeyResolver,
3783
+ providerName: this.#s.providerName,
3784
+ providerConfig: this.#s.providerConfig,
3785
+ validateFn: this.#s.validateFn,
3786
+ settlement: this.#s.settlement,
3787
+ mppInfo: this.#s.mppInfo,
3788
+ tickCost: this.#s.tickCost,
3789
+ unitType: this.#s.unitType
3748
3790
  };
3749
- this._registry.register(entry);
3750
- return createRequestHandler(entry, handlerFn, this._deps);
3791
+ this.#s.registry.register(entry);
3792
+ return createRequestHandler(entry, handlerFn, this.#s.deps);
3751
3793
  }
3752
3794
  };
3753
3795
  function resolvePaidArgs(routeKey, pricingOrOptions, options) {
@@ -3967,7 +4009,7 @@ function toProtocolObject(protocol, mppInfo) {
3967
4009
  mpp: {
3968
4010
  method: mppInfo?.method ?? "tempo",
3969
4011
  intent: mppInfo?.intent ?? "charge",
3970
- currency: mppInfo?.currency ?? TEMPO_USDC_CURRENCY
4012
+ currency: mppInfo?.currency ?? TEMPO_USDC_ADDRESS
3971
4013
  }
3972
4014
  };
3973
4015
  }
@@ -3991,17 +4033,16 @@ function buildPricingInfo(entry) {
3991
4033
  };
3992
4034
  }
3993
4035
  if ("tiers" in entry.pricing) {
3994
- const tierPrices = Object.values(entry.pricing.tiers).map((tier) => parseFloat(tier.price));
3995
- const min = Math.min(...tierPrices);
3996
- const max = Math.max(...tierPrices);
3997
- if (Number.isFinite(min) && Number.isFinite(max)) {
3998
- if (min === max) {
4036
+ const tierPrices = Object.values(entry.pricing.tiers).map((tier) => tier.price);
4037
+ const extrema = tierExtrema(tierPrices);
4038
+ if (extrema) {
4039
+ if (extrema.min === extrema.max) {
3999
4040
  return {
4000
- price: { mode: "fixed", currency: "USD", amount: String(min) }
4041
+ price: { mode: "fixed", currency: "USD", amount: extrema.min }
4001
4042
  };
4002
4043
  }
4003
4044
  return {
4004
- price: { mode: "dynamic", currency: "USD", min: String(min), max: String(max) }
4045
+ price: { mode: "dynamic", currency: "USD", min: extrema.min, max: extrema.max }
4005
4046
  };
4006
4047
  }
4007
4048
  return {
@@ -4015,6 +4056,20 @@ function buildPricingInfo(entry) {
4015
4056
  }
4016
4057
  return void 0;
4017
4058
  }
4059
+ function tierExtrema(prices) {
4060
+ if (prices.length === 0) return null;
4061
+ let min = prices[0];
4062
+ let max = prices[0];
4063
+ try {
4064
+ for (const price of prices.slice(1)) {
4065
+ if (compareDecimals(price, min) < 0) min = price;
4066
+ if (compareDecimals(price, max) > 0) max = price;
4067
+ }
4068
+ } catch {
4069
+ return null;
4070
+ }
4071
+ return { min, max };
4072
+ }
4018
4073
 
4019
4074
  // src/discovery/llms-txt.ts
4020
4075
  var import_server10 = require("next/server");
@@ -4047,142 +4102,256 @@ function formatRouterConfigIssues(issues) {
4047
4102
  return issues.map((issue) => issue.message).join("\n");
4048
4103
  }
4049
4104
 
4050
- // src/config/validators/x402.ts
4051
- init_accepts();
4105
+ // src/config/schema.ts
4106
+ var import_zod = require("zod");
4107
+ init_constants();
4052
4108
 
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;
4109
+ // src/config/utils.ts
4110
+ var import_accounts = require("viem/accounts");
4111
+ var EVM_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
4112
+ var EVM_PRIVATE_KEY_RE = /^0x[a-fA-F0-9]{64}$/;
4113
+ var SOLANA_ADDRESS_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
4114
+ var ZERO_EVM_ADDRESS_RE = /^0x0{40}$/i;
4115
+ function isUrl(value) {
4116
+ try {
4117
+ new URL(value);
4118
+ return true;
4119
+ } catch {
4120
+ return false;
4121
+ }
4122
+ }
4123
+ var isEvmAddress = (v) => EVM_ADDRESS_RE.test(v);
4124
+ var isEvmPrivateKey = (v) => EVM_PRIVATE_KEY_RE.test(v);
4125
+ var isPlaceholderEvm = (v) => ZERO_EVM_ADDRESS_RE.test(v);
4126
+ var isSolanaAddress = (v) => SOLANA_ADDRESS_RE.test(v);
4127
+ var isX402Network = (v) => v.startsWith("eip155:") || v.startsWith("solana:");
4128
+ var canonicalizeEvm = (addr) => addr.toLowerCase();
4129
+ function operatorAddressesCollide(opKey, fpKey) {
4130
+ if (!opKey || !fpKey || !isEvmPrivateKey(opKey) || !isEvmPrivateKey(fpKey)) return null;
4131
+ const op = (0, import_accounts.privateKeyToAccount)(opKey).address.toLowerCase();
4132
+ const fp = (0, import_accounts.privateKeyToAccount)(fpKey).address.toLowerCase();
4133
+ return op === fp ? op : null;
4134
+ }
4135
+ function trimAll(raw) {
4136
+ const out = {};
4137
+ for (const [k, v] of Object.entries(raw)) {
4138
+ if (typeof v !== "string") {
4139
+ out[k] = void 0;
4140
+ continue;
4141
+ }
4142
+ const trimmed = v.trim();
4143
+ out[k] = trimmed.length > 0 ? trimmed : void 0;
4144
+ }
4145
+ return out;
4073
4146
  }
4074
4147
 
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) {
4148
+ // src/config/schema.ts
4149
+ function addIssue(ctx, params, message, path = []) {
4150
+ ctx.addIssue({ code: "custom", path, params, message });
4151
+ }
4152
+ var x402 = { protocol: "x402" };
4153
+ var mpp = { protocol: "mpp" };
4154
+ var envShape = {
4155
+ BASE_URL: import_zod.z.string().refine(isUrl, {
4156
+ params: { code: "invalid_base_url" },
4157
+ 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."
4158
+ }).optional(),
4159
+ EVM_PAYEE_ADDRESS: import_zod.z.string().refine(isEvmAddress, {
4160
+ params: { code: "invalid_x402_payee", ...x402 },
4161
+ message: "EVM_PAYEE_ADDRESS must be a 0x-prefixed 20-byte EVM address \u2014 the wallet that receives x402 and MPP payments."
4162
+ }).refine((v) => !isPlaceholderEvm(v), {
4163
+ params: { code: "placeholder_payee", ...x402 },
4164
+ message: "EVM_PAYEE_ADDRESS is the zero address (0x000\u2026000) \u2014 payments to this address are unrecoverable. Set it to a wallet you control."
4165
+ }).optional(),
4166
+ CDP_API_KEY_ID: import_zod.z.string().optional(),
4167
+ CDP_API_KEY_SECRET: import_zod.z.string().optional(),
4168
+ SOLANA_PAYEE_ADDRESS: import_zod.z.string().refine(isSolanaAddress, {
4169
+ params: { code: "invalid_solana_payee", ...x402 },
4170
+ message: "SOLANA_PAYEE_ADDRESS must be a base58 Solana address (32\u201344 chars). When set, the router also accepts Solana payments."
4171
+ }).optional(),
4172
+ SOLANA_FACILITATOR_URL: import_zod.z.string().refine(isUrl, {
4173
+ params: { code: "invalid_solana_facilitator_url", ...x402 },
4174
+ message: "SOLANA_FACILITATOR_URL must be a valid URL \u2014 override for the Solana x402 facilitator. Defaults to DEFAULT_SOLANA_FACILITATOR_URL."
4175
+ }).optional(),
4176
+ MPP_SECRET_KEY: import_zod.z.string().optional(),
4177
+ MPP_CURRENCY: import_zod.z.string().refine(isEvmAddress, {
4178
+ params: { code: "invalid_mpp_currency", ...mpp },
4179
+ 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."
4180
+ }).optional(),
4181
+ TEMPO_RPC_URL: import_zod.z.string().refine(isUrl, {
4182
+ params: { code: "invalid_mpp_rpc_url", ...mpp },
4183
+ message: "TEMPO_RPC_URL must be a valid URL \u2014 authenticated Tempo JSON-RPC endpoint. Public rpc.tempo.xyz returns 401."
4184
+ }).optional(),
4185
+ MPP_OPERATOR_KEY: import_zod.z.string().refine(isEvmPrivateKey, {
4186
+ params: { code: "invalid_mpp_operator_key", ...mpp },
4187
+ 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."
4188
+ }).optional(),
4189
+ MPP_FEE_PAYER_KEY: import_zod.z.string().refine(isEvmPrivateKey, {
4190
+ params: { code: "invalid_mpp_fee_payer_key", ...mpp },
4191
+ 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."
4192
+ }).optional(),
4193
+ KV_REST_API_URL: import_zod.z.string().optional(),
4194
+ KV_REST_API_TOKEN: import_zod.z.string().optional(),
4195
+ NODE_ENV: import_zod.z.string().optional()
4196
+ };
4197
+ var ENV_KEYS = Object.keys(envShape);
4198
+ var EnvInputSchema = import_zod.z.object(envShape).passthrough().superRefine((env, ctx) => {
4199
+ if (env.BASE_URL === void 0) {
4200
+ addIssue(
4201
+ ctx,
4202
+ { code: "missing_base_url" },
4203
+ "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.",
4204
+ ["BASE_URL"]
4205
+ );
4206
+ }
4207
+ if (env.EVM_PAYEE_ADDRESS === void 0) {
4208
+ addIssue(
4209
+ ctx,
4210
+ { code: "missing_x402_payee", ...x402 },
4211
+ "EVM_PAYEE_ADDRESS is required \u2014 the EVM address that receives x402 and MPP payments.",
4212
+ ["EVM_PAYEE_ADDRESS"]
4213
+ );
4214
+ }
4215
+ if (env.MPP_SECRET_KEY) {
4216
+ if (env.MPP_CURRENCY === void 0) {
4217
+ addIssue(
4218
+ ctx,
4219
+ { code: "missing_mpp_currency", ...mpp },
4220
+ "MPP_CURRENCY is required when MPP is enabled \u2014 the Tempo currency address MPP charges in. Use TEMPO_USDC_ADDRESS for Tempo USDC.",
4221
+ ["MPP_CURRENCY"]
4222
+ );
4223
+ }
4224
+ if (env.TEMPO_RPC_URL === void 0) {
4225
+ addIssue(
4226
+ ctx,
4227
+ { code: "missing_mpp_rpc_url", ...mpp },
4228
+ "TEMPO_RPC_URL is required when MPP is enabled \u2014 authenticated Tempo JSON-RPC endpoint. Public rpc.tempo.xyz returns 401.",
4229
+ ["TEMPO_RPC_URL"]
4230
+ );
4231
+ }
4232
+ }
4233
+ const collision = operatorAddressesCollide(env.MPP_OPERATOR_KEY, env.MPP_FEE_PAYER_KEY);
4234
+ if (collision) {
4235
+ addIssue(
4236
+ ctx,
4237
+ { code: "mpp_operator_equals_fee_payer", ...mpp },
4238
+ `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.`,
4239
+ ["MPP_FEE_PAYER_KEY"]
4240
+ );
4241
+ }
4242
+ });
4243
+ function collectKvWarnings(env, kvStoreOptionProvided) {
4244
+ if (kvStoreOptionProvided) return [];
4245
+ const warn = (code, message) => ({
4246
+ code,
4247
+ severity: "warning",
4248
+ message
4249
+ });
4250
+ if (env.KV_REST_API_URL && !env.KV_REST_API_TOKEN) {
4088
4251
  return [
4089
- {
4090
- code: "missing_x402_accepts",
4091
- protocol: "x402",
4092
- message: "x402 requires at least one accept configuration."
4093
- }
4252
+ warn(
4253
+ "kv_url_without_token",
4254
+ "KV_REST_API_URL is set but KV_REST_API_TOKEN is missing \u2014 falling back to in-memory KV (unsafe in serverless production)."
4255
+ )
4094
4256
  ];
4095
4257
  }
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;
4258
+ if (env.KV_REST_API_TOKEN && !env.KV_REST_API_URL) {
4259
+ return [
4260
+ warn(
4261
+ "kv_token_without_url",
4262
+ "KV_REST_API_TOKEN is set but KV_REST_API_URL is missing \u2014 falling back to in-memory KV (unsafe in serverless production)."
4263
+ )
4264
+ ];
4265
+ }
4266
+ if (env.KV_REST_API_URL && env.KV_REST_API_TOKEN && !isUrl(env.KV_REST_API_URL)) {
4267
+ return [
4268
+ warn(
4269
+ "invalid_kv_url",
4270
+ `KV_REST_API_URL is not a valid URL \u2014 KV calls will fail at request time. Got: ${env.KV_REST_API_URL}`
4271
+ )
4272
+ ];
4273
+ }
4274
+ if (!env.KV_REST_API_URL && !env.KV_REST_API_TOKEN && env.NODE_ENV === "production") {
4275
+ return [
4276
+ warn(
4277
+ "missing_kv_in_production",
4278
+ "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."
4279
+ )
4280
+ ];
4281
+ }
4282
+ return [];
4121
4283
  }
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
- };
4284
+ function getConfiguredX402Accepts2(config) {
4285
+ if (config.x402?.accepts?.length) return [...config.x402.accepts];
4286
+ return [
4287
+ {
4288
+ scheme: "exact",
4289
+ network: config.network ?? BASE_MAINNET_NETWORK,
4290
+ payTo: config.payeeAddress
4291
+ }
4292
+ ];
4132
4293
  }
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([
4294
+ function validateX402Config(config, env, options) {
4295
+ const accepts = getConfiguredX402Accepts2(config);
4296
+ const issues = [];
4297
+ const push = (code, message) => issues.push({ code, protocol: "x402", message });
4298
+ if (accepts.length === 0) {
4299
+ push("missing_x402_accepts", "x402 requires at least one accept configuration.");
4300
+ return issues;
4301
+ }
4302
+ if (accepts.some((a) => !a.network)) {
4303
+ push("missing_x402_network", "x402 accepts require a network.");
4304
+ }
4305
+ const unsupported = accepts.find((a) => a.network && !isX402Network(a.network));
4306
+ if (unsupported) {
4307
+ push(
4308
+ "unsupported_x402_network",
4309
+ `unsupported x402 network '${unsupported.network}'. Use eip155:* or solana:*.`
4310
+ );
4311
+ }
4312
+ if (accepts.some((a) => (a.scheme ?? "exact") !== "exact" && !a.asset)) {
4313
+ push("missing_x402_asset", "non-exact x402 accepts require an asset.");
4314
+ }
4315
+ if (accepts.some(
4316
+ (a) => a.decimals !== void 0 && (!Number.isInteger(a.decimals) || a.decimals < 0)
4317
+ )) {
4318
+ push("invalid_x402_decimals", "x402 accept decimals must be a non-negative integer.");
4319
+ }
4320
+ if (!config.payeeAddress && accepts.some((a) => !a.payTo)) {
4321
+ push(
4322
+ "missing_x402_payee",
4323
+ "x402 requires payeeAddress in router config or payTo on every x402 accept."
4324
+ );
4325
+ }
4326
+ const placeholder = [
4146
4327
  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
- };
4328
+ ...accepts.map((a) => typeof a.payTo === "string" ? a.payTo : void 0)
4329
+ ].find((v) => v !== void 0 && isPlaceholderEvm(v));
4330
+ if (placeholder) {
4331
+ push(
4332
+ "placeholder_payee",
4333
+ `x402 payee '${placeholder}' is a placeholder address and cannot receive payments.`
4334
+ );
4335
+ }
4336
+ if (options.requireCdpKeys !== false) {
4337
+ const hasEvm = accepts.some(
4338
+ (a) => typeof a.network === "string" && a.network.startsWith("eip155:")
4339
+ );
4340
+ if (hasEvm) {
4341
+ const missing = ["CDP_API_KEY_ID", "CDP_API_KEY_SECRET"].filter((k) => !env[k]);
4342
+ if (missing.length > 0) {
4343
+ push(
4344
+ "missing_cdp_keys",
4345
+ `x402 EVM facilitator (Coinbase) requires ${missing.join(" and ")}.`
4346
+ );
4347
+ }
4348
+ }
4349
+ }
4350
+ return issues;
4169
4351
  }
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) {
4352
+ function validateMppConfig(config) {
4353
+ const m = config.mpp;
4354
+ if (!m) {
4186
4355
  return [
4187
4356
  {
4188
4357
  code: "missing_mpp_config",
@@ -4191,109 +4360,184 @@ function validateMppConfig(config, env) {
4191
4360
  }
4192
4361
  ];
4193
4362
  }
4194
- const args = { config, mpp, env };
4195
- return CHECKS2.map((check) => check(args)).filter(
4196
- (issue) => issue !== null
4363
+ const issues = [];
4364
+ const push = (code, message) => issues.push({ code, protocol: "mpp", message });
4365
+ if (!m.secretKey) {
4366
+ push(
4367
+ "missing_mpp_secret_key",
4368
+ "MPP requires secretKey. Set MPP_SECRET_KEY or pass mpp.secretKey."
4369
+ );
4370
+ }
4371
+ if (!m.currency) {
4372
+ push("missing_mpp_currency", "MPP requires currency. Set MPP_CURRENCY or pass mpp.currency.");
4373
+ } else if (!isEvmAddress(m.currency)) {
4374
+ push(
4375
+ "invalid_mpp_currency",
4376
+ "MPP currency must be a 0x-prefixed 20-byte Tempo currency address. Use TEMPO_USDC_ADDRESS for Tempo USDC."
4377
+ );
4378
+ }
4379
+ const recipient = m.recipient ?? config.payeeAddress;
4380
+ if (!recipient) {
4381
+ push(
4382
+ "missing_mpp_recipient",
4383
+ "MPP requires a recipient address. Set mpp.recipient or payeeAddress in your router config."
4384
+ );
4385
+ } else if (!isEvmAddress(recipient)) {
4386
+ push(
4387
+ "invalid_mpp_recipient",
4388
+ "MPP recipient must be a 0x-prefixed EVM address. Solana recipients require x402."
4389
+ );
4390
+ }
4391
+ const placeholder = [m.recipient, config.payeeAddress].find(
4392
+ (v) => typeof v === "string" && isPlaceholderEvm(v)
4197
4393
  );
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
- };
4394
+ if (placeholder) {
4395
+ push(
4396
+ "placeholder_payee",
4397
+ `MPP recipient '${placeholder}' is a placeholder address and cannot receive payments.`
4398
+ );
4214
4399
  }
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
- };
4400
+ if (!m.rpcUrl) {
4401
+ push(
4402
+ "missing_mpp_rpc_url",
4403
+ "MPP requires an authenticated Tempo RPC URL. Set TEMPO_RPC_URL env var or pass rpcUrl in the mpp config object."
4404
+ );
4221
4405
  }
4222
- return null;
4406
+ if (m.feePayerKey && !isEvmPrivateKey(m.feePayerKey)) {
4407
+ push(
4408
+ "invalid_mpp_fee_payer_key",
4409
+ "MPP feePayerKey must be a 0x-prefixed 32-byte EVM private key."
4410
+ );
4411
+ }
4412
+ if (m.operatorKey && !isEvmPrivateKey(m.operatorKey)) {
4413
+ push(
4414
+ "invalid_mpp_operator_key",
4415
+ "MPP operatorKey must be a 0x-prefixed 32-byte EVM private key."
4416
+ );
4417
+ }
4418
+ const collision = operatorAddressesCollide(m.operatorKey, m.feePayerKey);
4419
+ if (collision) {
4420
+ push(
4421
+ "mpp_operator_equals_fee_payer",
4422
+ `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).`
4423
+ );
4424
+ }
4425
+ return issues;
4223
4426
  }
4224
- function checkRecipient({ config, mpp }) {
4225
- const recipient = mpp.recipient ?? config.payeeAddress;
4226
- if (!recipient) {
4427
+ function translateZodIssues(error) {
4428
+ return error.issues.map((issue) => {
4429
+ const params = issue.params;
4430
+ if (!params?.code) {
4431
+ throw new Error(
4432
+ `[router] schema issue missing params.code (path=${issue.path.join(".")}, message=${issue.message}). Every refinement / addIssue call must set params.code.`
4433
+ );
4434
+ }
4227
4435
  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."
4436
+ code: params.code,
4437
+ message: issue.message,
4438
+ ...params.protocol ? { protocol: params.protocol } : {},
4439
+ ...params.severity ? { severity: params.severity } : {}
4231
4440
  };
4441
+ });
4442
+ }
4443
+ function routerConfigFromEnv(options) {
4444
+ const rawEnv = options.env ?? process.env;
4445
+ const env = trimAll(rawEnv);
4446
+ const optionIssues = [];
4447
+ if (!options.title?.trim()) {
4448
+ optionIssues.push({
4449
+ code: "missing_discovery_title",
4450
+ message: "discovery `title` is required. Pass a short product name."
4451
+ });
4232
4452
  }
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
- };
4453
+ if (!options.description?.trim()) {
4454
+ optionIssues.push({
4455
+ code: "missing_discovery_description",
4456
+ message: "discovery `description` is required. One sentence is enough."
4457
+ });
4239
4458
  }
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;
4459
+ if (options.guidance === void 0) {
4460
+ optionIssues.push({
4461
+ code: "missing_discovery_guidance",
4462
+ message: "discovery `guidance` is required. Provide an empty string to opt out of `/llms.txt`."
4463
+ });
4464
+ }
4465
+ if (options.serverUrl !== void 0 && !isUrl(options.serverUrl)) {
4466
+ optionIssues.push({
4467
+ code: "invalid_server_url",
4468
+ message: `discovery \`serverUrl\` must be a valid URL. Got: ${options.serverUrl}`
4469
+ });
4470
+ }
4471
+ const parsed = EnvInputSchema.safeParse(env);
4472
+ const envIssues = parsed.success ? [] : translateZodIssues(parsed.error);
4473
+ const issues = [...envIssues, ...optionIssues];
4474
+ if (issues.length > 0) throw new RouterConfigError(issues);
4475
+ for (const warning of collectKvWarnings(env, options.kvStore !== void 0)) {
4476
+ console.warn(`[router] ${warning.message}`);
4477
+ }
4478
+ const payeeAddress = canonicalizeEvm(env.EVM_PAYEE_ADDRESS);
4479
+ const accepts = [
4480
+ { scheme: "exact", network: BASE_MAINNET_NETWORK, payTo: payeeAddress },
4481
+ {
4482
+ scheme: "upto",
4483
+ network: BASE_MAINNET_NETWORK,
4484
+ payTo: payeeAddress,
4485
+ asset: BASE_USDC_ADDRESS,
4486
+ decimals: BASE_USDC_DECIMALS
4487
+ }
4488
+ ];
4489
+ if (env.SOLANA_PAYEE_ADDRESS) {
4490
+ accepts.push({
4491
+ scheme: "exact",
4492
+ network: SOLANA_MAINNET_NETWORK,
4493
+ payTo: env.SOLANA_PAYEE_ADDRESS
4494
+ });
4495
+ }
4496
+ const configuredSolanaFacilitator = options.x402Facilitators?.solana;
4497
+ const solanaFacilitator = typeof configuredSolanaFacilitator === "string" ? configuredSolanaFacilitator : configuredSolanaFacilitator ?? env.SOLANA_FACILITATOR_URL ?? DEFAULT_SOLANA_FACILITATOR_URL;
4498
+ const mppEnabled = options.protocols?.includes("mpp") ?? Boolean(env.MPP_SECRET_KEY);
4499
+ const protocols = options.protocols ? [...options.protocols] : mppEnabled ? ["x402", "mpp"] : ["x402"];
4500
+ const mppConfig = mppEnabled ? {
4501
+ secretKey: env.MPP_SECRET_KEY,
4502
+ currency: canonicalizeEvm(env.MPP_CURRENCY),
4503
+ rpcUrl: env.TEMPO_RPC_URL,
4504
+ recipient: payeeAddress,
4505
+ ...env.MPP_FEE_PAYER_KEY ? { feePayerKey: env.MPP_FEE_PAYER_KEY } : {},
4506
+ ...env.MPP_OPERATOR_KEY ? { operatorKey: env.MPP_OPERATOR_KEY, session: {} } : {}
4507
+ } : void 0;
4281
4508
  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).`
4509
+ payeeAddress,
4510
+ baseUrl: env.BASE_URL,
4511
+ network: BASE_MAINNET_NETWORK,
4512
+ protocols,
4513
+ x402: {
4514
+ accepts,
4515
+ facilitators: {
4516
+ ...options.x402Facilitators,
4517
+ solana: solanaFacilitator
4518
+ }
4519
+ },
4520
+ ...mppConfig ? { mpp: mppConfig } : {},
4521
+ discovery: {
4522
+ title: options.title,
4523
+ version: options.version ?? "1.0.0",
4524
+ description: options.description,
4525
+ guidance: options.guidance,
4526
+ ...options.contact ? { contact: options.contact } : {},
4527
+ ...options.ownershipProofs ? { ownershipProofs: options.ownershipProofs } : {},
4528
+ ...options.methodHints ? { methodHints: options.methodHints } : {},
4529
+ ...options.serverUrl ? { serverUrl: options.serverUrl } : {}
4530
+ },
4531
+ ...options.prices ? { prices: options.prices } : {},
4532
+ ...options.plugin ? { plugin: options.plugin } : {},
4533
+ ...options.kvStore ? { kvStore: options.kvStore } : {},
4534
+ strictRoutes: options.strictRoutes ?? false
4285
4535
  };
4286
4536
  }
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
4537
  function getRouterConfigIssues(config, options = {}) {
4294
- const env = options.env ?? process.env;
4295
- const issues = [];
4538
+ const env = options.env ?? {};
4296
4539
  const protocols = config.protocols ?? ["x402"];
4540
+ const issues = [];
4297
4541
  if (!config.baseUrl) {
4298
4542
  issues.push({
4299
4543
  code: "missing_base_url",
@@ -4306,84 +4550,17 @@ function getRouterConfigIssues(config, options = {}) {
4306
4550
  message: "RouterConfig.protocols cannot be empty. Omit the field to use default ['x402'] or specify protocols explicitly."
4307
4551
  });
4308
4552
  }
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
- }
4553
+ if (protocols.includes("x402")) issues.push(...validateX402Config(config, env, options));
4554
+ if (protocols.includes("mpp")) issues.push(...validateMppConfig(config));
4315
4555
  return issues;
4316
4556
  }
4317
4557
 
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
4558
  // src/init/x402.ts
4382
- async function initX402(config, configError) {
4559
+ async function initX402(config, kvStore, configError) {
4383
4560
  if (configError) return { initError: configError };
4384
4561
  try {
4385
- const { createX402Server: createX402Server2 } = await Promise.resolve().then(() => (init_server(), server_exports));
4386
- const result = await createX402Server2(config);
4562
+ const { createX402Server: createX402Server2 } = await Promise.resolve().then(() => (init_x402_server(), x402_server_exports));
4563
+ const result = await createX402Server2(config, kvStore);
4387
4564
  await result.initPromise;
4388
4565
  return {
4389
4566
  server: result.server,
@@ -4394,7 +4571,7 @@ async function initX402(config, configError) {
4394
4571
  }
4395
4572
  }
4396
4573
 
4397
- // src/mppx-init.ts
4574
+ // src/init/mppx.ts
4398
4575
  function getMppxRequestContext(args) {
4399
4576
  const {
4400
4577
  Mppx,
@@ -4516,9 +4693,10 @@ function createRouter(config) {
4516
4693
  const kvStore = resolveKvStore(config.kvStore);
4517
4694
  const nonceStore = kvStore ? createKvNonceStore(kvStore) : new MemoryNonceStore();
4518
4695
  const entitlementStore = kvStore ? createKvEntitlementStore(kvStore) : new MemoryEntitlementStore();
4519
- const network = config.network ?? BASE_NETWORK;
4696
+ const network = config.network ?? BASE_MAINNET_NETWORK;
4520
4697
  const x402Accepts = getConfiguredX402Accepts(config);
4521
4698
  const configIssues = getRouterConfigIssues(config, {
4699
+ env: process.env,
4522
4700
  requireCdpKeys: process.env.NODE_ENV === "production"
4523
4701
  });
4524
4702
  const baseUrlIssue = configIssues.find((issue) => issue.code === "missing_base_url");
@@ -4565,7 +4743,7 @@ function createRouter(config) {
4565
4743
  mppSessionConfig: config.mpp?.session ? { depositMultiplier: config.mpp.session.depositMultiplier ?? 10 } : null
4566
4744
  };
4567
4745
  deps.initPromise = (async () => {
4568
- const x402Result = await initX402(config, x402ConfigError);
4746
+ const x402Result = await initX402(config, kvStore, x402ConfigError);
4569
4747
  deps.x402Server = x402Result.server ?? null;
4570
4748
  deps.x402FacilitatorsByNetwork = x402Result.facilitatorsByNetwork;
4571
4749
  if (x402Result.initError) deps.x402InitError = x402Result.initError;
@@ -4594,11 +4772,10 @@ function createRouter(config) {
4594
4772
  `[router] strictRoutes=true forbids key/path divergence for route '${definition.path}'. Remove custom \`key\` or make it equal to \`path\`.`
4595
4773
  );
4596
4774
  }
4597
- let builder = new RouteBuilder(key, registry, deps);
4775
+ let builder = new RouteBuilder(key, registry, deps, {
4776
+ protocols: config.protocols
4777
+ });
4598
4778
  builder = builder.path(normalizedPath);
4599
- if (config.protocols) {
4600
- builder._protocols = [...config.protocols];
4601
- }
4602
4779
  if (definition.method) {
4603
4780
  builder = builder.method(definition.method);
4604
4781
  }
@@ -4641,30 +4818,22 @@ function normalizePath(path) {
4641
4818
  normalized = normalized.replace(/^api\/+/, "");
4642
4819
  return normalized.replace(/\/+$/, "");
4643
4820
  }
4821
+ function createRouterFromEnv(options) {
4822
+ return createRouter(routerConfigFromEnv(options));
4823
+ }
4644
4824
  // Annotate the CommonJS export names for ESM import in node:
4645
4825
  0 && (module.exports = {
4646
- BASE_NETWORK,
4826
+ BASE_MAINNET_NETWORK,
4827
+ BASE_USDC_ADDRESS,
4828
+ BASE_USDC_DECIMALS,
4829
+ DEFAULT_SOLANA_FACILITATOR_URL,
4647
4830
  HttpError,
4648
- MemoryEntitlementStore,
4649
- MemoryNonceStore,
4650
- RouteBuilder,
4651
- RouteRegistry,
4652
4831
  RouterConfigError,
4653
- SIWX_CHALLENGE_EXPIRY_MS,
4654
- SIWX_ERROR_MESSAGES,
4655
4832
  SOLANA_MAINNET_NETWORK,
4656
- TEMPO_USDC_CURRENCY,
4833
+ TEMPO_USDC_ADDRESS,
4834
+ TEMPO_USDC_DECIMALS,
4657
4835
  ZERO_EVM_ADDRESS,
4658
- consolePlugin,
4659
- createKvEntitlementStore,
4660
- createKvMppStore,
4661
- createKvNonceStore,
4662
4836
  createRouter,
4663
- formatRouterConfigIssues,
4664
- getRouterConfigIssues,
4665
- mppFromEnv,
4666
- paidOptionsForProtocols,
4667
- validateRouterConfig,
4668
- withPrefix,
4669
- x402AcceptsFromEnv
4837
+ createRouterFromEnv,
4838
+ routerConfigFromEnv
4670
4839
  });