@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.js CHANGED
@@ -9,14 +9,18 @@ var __export = (target, all) => {
9
9
  };
10
10
 
11
11
  // src/constants.ts
12
- var BASE_NETWORK, SOLANA_MAINNET_NETWORK, TEMPO_USDC_CURRENCY, ZERO_EVM_ADDRESS;
12
+ 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;
13
13
  var init_constants = __esm({
14
14
  "src/constants.ts"() {
15
15
  "use strict";
16
- BASE_NETWORK = "eip155:8453";
16
+ BASE_MAINNET_NETWORK = "eip155:8453";
17
17
  SOLANA_MAINNET_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
18
- TEMPO_USDC_CURRENCY = "0x20c000000000000000000000b9537d11c60e8b50";
18
+ TEMPO_USDC_ADDRESS = "0x20c000000000000000000000b9537d11c60e8b50";
19
+ TEMPO_USDC_DECIMALS = 6;
20
+ BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
21
+ BASE_USDC_DECIMALS = 6;
19
22
  ZERO_EVM_ADDRESS = "0x0000000000000000000000000000000000000000";
23
+ DEFAULT_SOLANA_FACILITATOR_URL = "https://facilitator.corbits.dev";
20
24
  }
21
25
  });
22
26
 
@@ -33,7 +37,7 @@ function getConfiguredX402Accepts(config) {
33
37
  return [
34
38
  {
35
39
  scheme: "exact",
36
- network: config.network ?? BASE_NETWORK,
40
+ network: config.network ?? BASE_MAINNET_NETWORK,
37
41
  payTo: config.payeeAddress
38
42
  }
39
43
  ];
@@ -213,7 +217,10 @@ async function getAcceptsHeadersForFacilitator(facilitator) {
213
217
  return {};
214
218
  }
215
219
  function resolveX402FacilitatorTarget(config, network, defaultEvmFacilitator) {
216
- return (isSolanaNetwork(network) ? config.x402?.facilitators?.solana : void 0) ?? (isEvmNetwork(network) ? config.x402?.facilitators?.evm : void 0) ?? (isSolanaNetwork(network) ? DEFAULT_SOLANA_FACILITATOR_URL : defaultEvmFacilitator);
220
+ if (isSolanaNetwork(network)) {
221
+ return config.x402?.facilitators?.solana ?? DEFAULT_SOLANA_FACILITATOR_URL;
222
+ }
223
+ return defaultEvmFacilitator;
217
224
  }
218
225
  function normalizeFacilitatorTarget(target) {
219
226
  return typeof target === "string" ? { url: target } : target;
@@ -226,22 +233,148 @@ function getNetworkFamily(network) {
226
233
  function sameFacilitatorConfig(a, b) {
227
234
  return a.url === b.url && a.createAuthHeaders === b.createAuthHeaders && a.createAcceptsHeaders === b.createAcceptsHeaders;
228
235
  }
229
- var DEFAULT_SOLANA_FACILITATOR_URL;
230
236
  var init_facilitators = __esm({
231
237
  "src/protocols/x402/facilitators.ts"() {
232
238
  "use strict";
233
239
  init_evm();
234
240
  init_solana();
235
- DEFAULT_SOLANA_FACILITATOR_URL = "https://facilitator.corbits.dev";
241
+ init_constants();
242
+ }
243
+ });
244
+
245
+ // src/kv-store/facilitator-supported.ts
246
+ function withCachedSupported(inner, options = {}) {
247
+ const { kv, cacheKey, ttlSeconds = FACILITATOR_SUPPORTED_TTL_SECONDS, fallback } = options;
248
+ const kvKey = kv && cacheKey ? `${FACILITATOR_SUPPORTED_KV_PREFIX}${cacheKey}` : void 0;
249
+ let inflight;
250
+ return {
251
+ verify: inner.verify.bind(inner),
252
+ settle: inner.settle.bind(inner),
253
+ getSupported: () => {
254
+ if (inflight) return inflight;
255
+ const attempt = fetchSupported(inner, kv, kvKey, ttlSeconds, fallback);
256
+ inflight = attempt;
257
+ attempt.catch(() => {
258
+ if (inflight === attempt) inflight = void 0;
259
+ });
260
+ return attempt;
261
+ }
262
+ };
263
+ }
264
+ async function fetchSupported(inner, kv, kvKey, ttlSeconds, fallback) {
265
+ if (kv && kvKey) {
266
+ const cached = await readKvCache(kv, kvKey);
267
+ if (cached) return cached;
268
+ }
269
+ const fresh = await tryFetchLive(inner, fallback);
270
+ if (fresh === null) return fallback();
271
+ if (kv && kvKey) await writeKvCache(kv, kvKey, fresh, ttlSeconds);
272
+ return fresh;
273
+ }
274
+ async function tryFetchLive(inner, fallback) {
275
+ try {
276
+ return await inner.getSupported();
277
+ } catch (err) {
278
+ if (!fallback) throw err;
279
+ console.warn(
280
+ `[x402] facilitator /supported failed, using hardcoded baseline: ${err instanceof Error ? err.message : String(err)}`
281
+ );
282
+ return null;
283
+ }
284
+ }
285
+ async function readKvCache(kv, key) {
286
+ try {
287
+ const cached = await kv.get(key);
288
+ return isSupportedResponse(cached) ? cached : void 0;
289
+ } catch {
290
+ return void 0;
291
+ }
292
+ }
293
+ async function writeKvCache(kv, key, value, ttlSeconds) {
294
+ try {
295
+ await kv.setNxEx(key, value, ttlSeconds);
296
+ } catch {
297
+ }
298
+ }
299
+ function isSupportedResponse(value) {
300
+ return typeof value === "object" && value !== null && Array.isArray(value.kinds);
301
+ }
302
+ var FACILITATOR_SUPPORTED_TTL_SECONDS, FACILITATOR_SUPPORTED_KV_PREFIX;
303
+ var init_facilitator_supported = __esm({
304
+ "src/kv-store/facilitator-supported.ts"() {
305
+ "use strict";
306
+ FACILITATOR_SUPPORTED_TTL_SECONDS = 60 * 60;
307
+ FACILITATOR_SUPPORTED_KV_PREFIX = "x402:facilitator-supported:";
308
+ }
309
+ });
310
+
311
+ // src/protocols/x402/facilitator-clients.ts
312
+ function createFacilitatorClients(facilitatorsByNetwork, HTTPFacilitatorClient, kvStore) {
313
+ return getResolvedX402FacilitatorGroups(facilitatorsByNetwork).map((group) => {
314
+ const inner = new HTTPFacilitatorClient(group.config);
315
+ const kinds = buildSupportedKinds(group);
316
+ const baseline = () => ({
317
+ kinds,
318
+ extensions: [],
319
+ signers: {}
320
+ });
321
+ if (group.family === "solana") {
322
+ return hardcodedSupportedClient(inner, baseline);
323
+ }
324
+ const cached = withCachedSupported(inner, {
325
+ kv: kvStore,
326
+ cacheKey: group.config.url,
327
+ fallback: baseline
328
+ });
329
+ return withScopedKinds(cached, kinds);
330
+ });
331
+ }
332
+ function hardcodedSupportedClient(inner, build) {
333
+ return {
334
+ verify: inner.verify.bind(inner),
335
+ settle: inner.settle.bind(inner),
336
+ getSupported: async () => build()
337
+ };
338
+ }
339
+ function withScopedKinds(client, kinds) {
340
+ return {
341
+ verify: client.verify.bind(client),
342
+ settle: client.settle.bind(client),
343
+ getSupported: async () => ({ ...await client.getSupported(), kinds })
344
+ };
345
+ }
346
+ function buildSupportedKinds(group) {
347
+ return group.networks.flatMap((network) => {
348
+ if (group.family === "solana") {
349
+ return [
350
+ {
351
+ x402Version: 2,
352
+ scheme: "exact",
353
+ network,
354
+ extra: { features: { xSettlementAccountSupported: true } }
355
+ }
356
+ ];
357
+ }
358
+ return [
359
+ { x402Version: 2, scheme: "exact", network },
360
+ { x402Version: 2, scheme: "upto", network }
361
+ ];
362
+ });
363
+ }
364
+ var init_facilitator_clients = __esm({
365
+ "src/protocols/x402/facilitator-clients.ts"() {
366
+ "use strict";
367
+ init_facilitator_supported();
368
+ init_facilitators();
236
369
  }
237
370
  });
238
371
 
239
- // src/server.ts
240
- var server_exports = {};
241
- __export(server_exports, {
372
+ // src/init/x402-server.ts
373
+ var x402_server_exports = {};
374
+ __export(x402_server_exports, {
242
375
  createX402Server: () => createX402Server
243
376
  });
244
- async function createX402Server(config) {
377
+ async function createX402Server(config, kvStore) {
245
378
  const { x402ResourceServer, HTTPFacilitatorClient } = await import("@x402/core/server");
246
379
  const { registerExactEvmScheme } = await import("@x402/evm/exact/server");
247
380
  const { bazaarResourceServerExtension } = await import("@x402/extensions/bazaar");
@@ -255,7 +388,11 @@ async function createX402Server(config) {
255
388
  );
256
389
  const evmNetworks = filterEvmNetworks(configuredNetworks);
257
390
  const svmNetworks = filterSolanaNetworks(configuredNetworks);
258
- const facilitatorClients = createFacilitatorClients(facilitatorsByNetwork, HTTPFacilitatorClient);
391
+ const facilitatorClients = createFacilitatorClients(
392
+ facilitatorsByNetwork,
393
+ HTTPFacilitatorClient,
394
+ kvStore
395
+ );
259
396
  const server = new x402ResourceServer(
260
397
  facilitatorClients.length === 1 ? facilitatorClients[0] : facilitatorClients
261
398
  );
@@ -279,48 +416,13 @@ async function createX402Server(config) {
279
416
  facilitatorsByNetwork
280
417
  };
281
418
  }
282
- function createFacilitatorClients(facilitatorsByNetwork, HTTPFacilitatorClient) {
283
- const groups = getResolvedX402FacilitatorGroups(facilitatorsByNetwork);
284
- return groups.map((group) => {
285
- const inner = new HTTPFacilitatorClient(group.config);
286
- const kinds = buildSupportedKinds(group);
287
- return hardcodedSupportedClient(inner, kinds);
288
- });
289
- }
290
- function hardcodedSupportedClient(inner, kinds) {
291
- return {
292
- verify: inner.verify.bind(inner),
293
- settle: inner.settle.bind(inner),
294
- getSupported: async () => ({ kinds, extensions: [], signers: {} })
295
- };
296
- }
297
- function buildSupportedKinds(group) {
298
- return group.networks.flatMap((network) => {
299
- const exactKind = {
300
- x402Version: 2,
301
- scheme: "exact",
302
- network,
303
- ...group.family === "solana" ? {
304
- extra: {
305
- features: {
306
- xSettlementAccountSupported: true
307
- }
308
- }
309
- } : {}
310
- };
311
- const uptoKind = { x402Version: 2, scheme: "upto", network };
312
- if (group.family === "evm") {
313
- return [exactKind, uptoKind];
314
- }
315
- return [exactKind, uptoKind];
316
- });
317
- }
318
- var init_server = __esm({
319
- "src/server.ts"() {
419
+ var init_x402_server = __esm({
420
+ "src/init/x402-server.ts"() {
320
421
  "use strict";
321
422
  init_evm();
322
423
  init_solana();
323
424
  init_facilitators();
425
+ init_facilitator_clients();
324
426
  init_accepts();
325
427
  }
326
428
  });
@@ -389,7 +491,7 @@ var AUTH_SCHEME = {
389
491
  MPP_PAYMENT: "Payment "
390
492
  };
391
493
 
392
- // src/plugin.ts
494
+ // src/plugin/index.ts
393
495
  function createDefaultContext(meta) {
394
496
  const ctx = {
395
497
  requestId: meta.requestId,
@@ -425,46 +527,8 @@ function firePluginHook(plugin, method, ...args) {
425
527
  return void 0;
426
528
  }
427
529
  }
428
- function consolePlugin() {
429
- return {
430
- onRequest(meta) {
431
- const ctx = createDefaultContext(meta);
432
- return ctx;
433
- },
434
- onAuthVerified(_ctx, auth) {
435
- const wallet = auth.wallet ? ` wallet=${auth.wallet}` : "";
436
- console.log(`[router] AUTH ${auth.authMode} ${auth.route}${wallet}`);
437
- },
438
- onPaymentVerified(_ctx, payment) {
439
- console.log(`[router] VERIFIED ${payment.protocol} ${payment.payer} ${payment.amount}`);
440
- },
441
- onPaymentSettled(_ctx, settlement) {
442
- console.log(`[router] SETTLED ${settlement.protocol} tx=${settlement.transaction}`);
443
- },
444
- onResponse(ctx, response) {
445
- const wallet = ctx.verifiedWallet ? ` wallet=${ctx.verifiedWallet}` : "";
446
- console.log(
447
- `[router] ${ctx.route} \u2192 ${response.statusCode} (${response.duration}ms)${wallet}`
448
- );
449
- },
450
- onError(_ctx, error) {
451
- console.error(`[router] ERROR ${error.status}: ${error.message}`);
452
- },
453
- onAlert(_ctx, alert) {
454
- const logFn = alert.level === "critical" || alert.level === "error" ? console.error : alert.level === "warn" ? console.warn : console.log;
455
- logFn(
456
- `[router] ${alert.level.toUpperCase()} ${alert.route}: ${alert.message}`,
457
- alert.meta ?? ""
458
- );
459
- },
460
- onProviderQuota(_ctx, event) {
461
- const logFn = event.level === "critical" ? console.error : event.level === "warn" ? console.warn : console.log;
462
- logFn(`[router] QUOTA ${event.level.toUpperCase()} ${event.provider}: ${event.message}`);
463
- }
464
- };
465
- }
466
530
 
467
- // src/alert.ts
531
+ // src/plugin/reporter.ts
468
532
  function createReporter(plugin, pluginCtx, route) {
469
533
  return (level, message, meta) => {
470
534
  firePluginHook(plugin, "onAlert", pluginCtx, {
@@ -476,7 +540,7 @@ function createReporter(plugin, pluginCtx, route) {
476
540
  };
477
541
  }
478
542
 
479
- // src/pipeline/context/preflight.ts
543
+ // src/pipeline/steps/preflight.ts
480
544
  function preflight(routeEntry, handler, deps, request) {
481
545
  const meta = buildMeta(request, routeEntry);
482
546
  const pluginCtx = firePluginHook(deps.plugin, "onRequest", meta) ?? createDefaultContext(meta);
@@ -506,10 +570,10 @@ function buildMeta(request, routeEntry) {
506
570
  };
507
571
  }
508
572
 
509
- // src/pipeline/context/parse-body.ts
573
+ // src/pipeline/steps/parse-body.ts
510
574
  import { NextResponse } from "next/server";
511
575
 
512
- // src/body.ts
576
+ // src/pipeline/body.ts
513
577
  async function bufferBody(request) {
514
578
  const text = await request.text();
515
579
  if (!text) return void 0;
@@ -533,7 +597,19 @@ function validateBody(parsed, schema) {
533
597
  };
534
598
  }
535
599
 
536
- // src/pipeline/context/fire-plugin-response.ts
600
+ // src/plugin/events.ts
601
+ function fireAuthVerified(ctx, event) {
602
+ firePluginHook(ctx.deps.plugin, "onAuthVerified", ctx.pluginCtx, {
603
+ ...event,
604
+ route: ctx.routeEntry.key
605
+ });
606
+ }
607
+ function firePaymentVerified(ctx, event) {
608
+ firePluginHook(ctx.deps.plugin, "onPaymentVerified", ctx.pluginCtx, event);
609
+ }
610
+ function firePaymentSettled(ctx, event) {
611
+ firePluginHook(ctx.deps.plugin, "onPaymentSettled", ctx.pluginCtx, event);
612
+ }
537
613
  function firePluginResponse(ctx, response, requestBody, responseBody) {
538
614
  firePluginHook(ctx.deps.plugin, "onResponse", ctx.pluginCtx, {
539
615
  statusCode: response.status,
@@ -552,8 +628,37 @@ function firePluginResponse(ctx, response, requestBody, responseBody) {
552
628
  });
553
629
  }
554
630
  }
631
+ function fireProviderQuota(ctx, response, handlerResult) {
632
+ const { providerName, providerConfig } = ctx.routeEntry;
633
+ if (!providerName || !providerConfig?.extractQuota) return;
634
+ if (response.status >= 400) return;
635
+ try {
636
+ const quota = providerConfig.extractQuota(handlerResult, response.headers);
637
+ if (!quota) return;
638
+ const level = computeQuotaLevel(quota.remaining, providerConfig.warn, providerConfig.critical);
639
+ const overage = providerConfig.overage ?? "same-rate";
640
+ const event = {
641
+ provider: providerName,
642
+ route: ctx.routeEntry.key,
643
+ remaining: quota.remaining,
644
+ limit: quota.limit,
645
+ spend: quota.spend,
646
+ level,
647
+ overage,
648
+ message: quota.remaining !== null ? `${providerName}: ${quota.remaining}${quota.limit ? `/${quota.limit}` : ""} remaining` : `${providerName}: quota info unavailable`
649
+ };
650
+ firePluginHook(ctx.deps.plugin, "onProviderQuota", ctx.pluginCtx, event);
651
+ } catch {
652
+ }
653
+ }
654
+ function computeQuotaLevel(remaining, warn, critical) {
655
+ if (remaining === null) return "healthy";
656
+ if (critical !== void 0 && remaining <= critical) return "critical";
657
+ if (warn !== void 0 && remaining <= warn) return "warn";
658
+ return "healthy";
659
+ }
555
660
 
556
- // src/pipeline/context/parse-body.ts
661
+ // src/pipeline/steps/parse-body.ts
557
662
  async function parseBody(ctx, request = ctx.request) {
558
663
  if (!ctx.routeEntry.bodySchema) return { ok: true, data: void 0 };
559
664
  const raw = await bufferBody(request);
@@ -567,15 +672,7 @@ async function parseBody(ctx, request = ctx.request) {
567
672
  return { ok: false, response };
568
673
  }
569
674
 
570
- // src/pipeline/context/parse-query.ts
571
- function parseQuery(request, routeEntry) {
572
- if (!routeEntry.querySchema) return void 0;
573
- const params = Object.fromEntries(request.nextUrl.searchParams.entries());
574
- const result = routeEntry.querySchema.safeParse(params);
575
- return result.success ? result.data : params;
576
- }
577
-
578
- // src/pipeline/context/errors.ts
675
+ // src/pipeline/steps/errors.ts
579
676
  function errorStatus(error, fallback) {
580
677
  const status = error?.status;
581
678
  return typeof status === "number" ? status : fallback;
@@ -588,7 +685,7 @@ function handlerFailureError(response) {
588
685
  return Object.assign(new Error(message), { status: response.status });
589
686
  }
590
687
 
591
- // src/pipeline/context/fail.ts
688
+ // src/pipeline/steps/fail.ts
592
689
  import { NextResponse as NextResponse2 } from "next/server";
593
690
  function fail(ctx, status, message, requestBody) {
594
691
  const response = NextResponse2.json({ success: false, error: message }, { status });
@@ -596,7 +693,7 @@ function fail(ctx, status, message, requestBody) {
596
693
  return response;
597
694
  }
598
695
 
599
- // src/pipeline/context/run-validate.ts
696
+ // src/pipeline/steps/run-validate.ts
600
697
  async function runValidate(ctx, body) {
601
698
  if (!ctx.routeEntry.validateFn) return null;
602
699
  try {
@@ -619,6 +716,14 @@ var HttpError = class extends Error {
619
716
  }
620
717
  };
621
718
 
719
+ // src/pipeline/steps/parse-query.ts
720
+ function parseQuery(request, routeEntry) {
721
+ if (!routeEntry.querySchema) return void 0;
722
+ const params = Object.fromEntries(request.nextUrl.searchParams.entries());
723
+ const result = routeEntry.querySchema.safeParse(params);
724
+ return result.success ? result.data : params;
725
+ }
726
+
622
727
  // src/pipeline/flows/static/static-invoke.ts
623
728
  function invokePaidStatic(ctx, wallet, account, body, payment) {
624
729
  return runHandler(ctx, buildHandlerCtx(ctx, wallet, account, body, payment));
@@ -636,14 +741,7 @@ function buildHandlerCtx(ctx, wallet, account, body, payment) {
636
741
  wallet,
637
742
  payment,
638
743
  account,
639
- alert(level, message, alertMeta) {
640
- firePluginHook(ctx.deps.plugin, "onAlert", ctx.pluginCtx, {
641
- level,
642
- message,
643
- route: ctx.routeEntry.key,
644
- meta: alertMeta
645
- });
646
- },
744
+ alert: ctx.report,
647
745
  setVerifiedWallet: (addr) => ctx.pluginCtx.setVerifiedWallet(addr)
648
746
  };
649
747
  }
@@ -687,45 +785,14 @@ function isThenable(value) {
687
785
  return value != null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
688
786
  }
689
787
 
690
- // src/pipeline/context/fire-provider-quota.ts
691
- function fireProviderQuota(ctx, response, handlerResult) {
692
- const { providerName, providerConfig } = ctx.routeEntry;
693
- if (!providerName || !providerConfig?.extractQuota) return;
694
- if (response.status >= 400) return;
695
- try {
696
- const quota = providerConfig.extractQuota(handlerResult, response.headers);
697
- if (!quota) return;
698
- const level = computeQuotaLevel(quota.remaining, providerConfig.warn, providerConfig.critical);
699
- const overage = providerConfig.overage ?? "same-rate";
700
- const event = {
701
- provider: providerName,
702
- route: ctx.routeEntry.key,
703
- remaining: quota.remaining,
704
- limit: quota.limit,
705
- spend: quota.spend,
706
- level,
707
- overage,
708
- message: quota.remaining !== null ? `${providerName}: ${quota.remaining}${quota.limit ? `/${quota.limit}` : ""} remaining` : `${providerName}: quota info unavailable`
709
- };
710
- firePluginHook(ctx.deps.plugin, "onProviderQuota", ctx.pluginCtx, event);
711
- } catch {
712
- }
713
- }
714
- function computeQuotaLevel(remaining, warn, critical) {
715
- if (remaining === null) return "healthy";
716
- if (critical !== void 0 && remaining <= critical) return "critical";
717
- if (warn !== void 0 && remaining <= warn) return "warn";
718
- return "healthy";
719
- }
720
-
721
- // src/pipeline/context/finalize/response.ts
788
+ // src/pipeline/steps/finalize/response.ts
722
789
  function finalize(ctx, response, rawResult, requestBody) {
723
790
  fireProviderQuota(ctx, response, rawResult);
724
791
  firePluginResponse(ctx, response, requestBody, rawResult);
725
792
  return response;
726
793
  }
727
794
 
728
- // src/pipeline/context/grant-entitlement.ts
795
+ // src/pipeline/steps/grant-entitlement.ts
729
796
  async function grantEntitlementIfSiwx(ctx, wallet) {
730
797
  if (!ctx.routeEntry.siwxEnabled) return;
731
798
  try {
@@ -738,7 +805,7 @@ async function grantEntitlementIfSiwx(ctx, wallet) {
738
805
  }
739
806
  }
740
807
 
741
- // src/pipeline/context/settlement-context.ts
808
+ // src/pipeline/steps/settlement-context.ts
742
809
  function settlementContext(ctx, scope) {
743
810
  return {
744
811
  route: ctx.routeEntry.key,
@@ -752,7 +819,7 @@ function settlementContext(ctx, scope) {
752
819
  };
753
820
  }
754
821
 
755
- // src/pipeline/context/run-settlement-error.ts
822
+ // src/pipeline/steps/run-settlement-error.ts
756
823
  async function runSettlementError(ctx, scope, error, phase) {
757
824
  const hook = ctx.routeEntry.settlement?.onSettlementError;
758
825
  if (!hook) return;
@@ -764,7 +831,7 @@ async function runSettlementError(ctx, scope, error, phase) {
764
831
  }
765
832
  }
766
833
 
767
- // src/pipeline/context/run-after-settle.ts
834
+ // src/pipeline/steps/run-after-settle.ts
768
835
  async function runAfterSettle(ctx, scope) {
769
836
  const hook = ctx.routeEntry.settlement?.afterSettle;
770
837
  if (!hook) return;
@@ -777,11 +844,11 @@ async function runAfterSettle(ctx, scope) {
777
844
  }
778
845
  }
779
846
 
780
- // src/pipeline/context/finalize/epilogue.ts
847
+ // src/pipeline/steps/finalize/epilogue.ts
781
848
  async function runPostSettleEpilogue(args) {
782
849
  const { ctx, strategy, wallet, settle, afterSettleScope, rawResult, body } = args;
783
850
  await grantEntitlementIfSiwx(ctx, wallet);
784
- firePluginHook(ctx.deps.plugin, "onPaymentSettled", ctx.pluginCtx, {
851
+ firePaymentSettled(ctx, {
785
852
  protocol: strategy.protocol,
786
853
  payer: wallet,
787
854
  transaction: settle.settledPayment.transaction ?? "",
@@ -791,7 +858,7 @@ async function runPostSettleEpilogue(args) {
791
858
  return finalize(ctx, settle.response, rawResult, body);
792
859
  }
793
860
 
794
- // src/pipeline/context/finalize/request.ts
861
+ // src/pipeline/steps/finalize/request.ts
795
862
  async function settleAndFinalizeRequest(args) {
796
863
  const { ctx, strategy, verifyOutcome, scope, rawResult, body, billedAmount, onSettleError } = args;
797
864
  const { request, routeEntry, deps, report } = ctx;
@@ -824,7 +891,7 @@ async function settleAndFinalizeRequest(args) {
824
891
  });
825
892
  }
826
893
 
827
- // src/pipeline/context/finalize/stream.ts
894
+ // src/pipeline/steps/finalize/stream.ts
828
895
  async function settleAndFinalizeStream(args) {
829
896
  const { ctx, strategy, verifyOutcome, source, account, body, bindChannelCharge } = args;
830
897
  const { request, routeEntry, deps, report } = ctx;
@@ -862,7 +929,7 @@ async function settleAndFinalizeStream(args) {
862
929
  });
863
930
  }
864
931
 
865
- // src/pipeline/context/run-handler-only.ts
932
+ // src/pipeline/steps/run-handler-only.ts
866
933
  async function runHandlerOnly(ctx, wallet, account) {
867
934
  const body = await parseBody(ctx);
868
935
  if (!body.ok) return body.response;
@@ -872,7 +939,7 @@ async function runHandlerOnly(ctx, wallet, account) {
872
939
  return finalize(ctx, result.response, result.rawResult, body.data);
873
940
  }
874
941
 
875
- // src/pipeline/context/run-before-settle.ts
942
+ // src/pipeline/steps/run-before-settle.ts
876
943
  async function runBeforeSettle(ctx, scope) {
877
944
  const hook = ctx.routeEntry.settlement?.beforeSettle;
878
945
  if (!hook) return null;
@@ -889,7 +956,7 @@ async function runBeforeSettle(ctx, scope) {
889
956
  }
890
957
  }
891
958
 
892
- // src/pipeline/context/run-settled-handler-error.ts
959
+ // src/pipeline/steps/run-settled-handler-error.ts
893
960
  async function runSettledHandlerError(ctx, scope, error = scope.handlerError ?? handlerFailureError(scope.response)) {
894
961
  const hook = ctx.routeEntry.settlement?.onSettledHandlerError;
895
962
  if (!hook) return;
@@ -963,7 +1030,7 @@ async function buildSIWXExtension() {
963
1030
  return declareSIWxExtension();
964
1031
  }
965
1032
 
966
- // src/pipeline/context/try-siwx-fast-path.ts
1033
+ // src/pipeline/steps/try-siwx-fast-path.ts
967
1034
  async function trySiwxFastPath(ctx, account) {
968
1035
  const { request, routeEntry, deps } = ctx;
969
1036
  if (!routeEntry.siwxEnabled) return null;
@@ -975,22 +1042,18 @@ async function trySiwxFastPath(ctx, account) {
975
1042
  ctx.pluginCtx.setVerifiedWallet(wallet);
976
1043
  const entitled = await deps.entitlementStore.has(routeEntry.key, wallet);
977
1044
  if (!entitled) return null;
978
- firePluginHook(deps.plugin, "onAuthVerified", ctx.pluginCtx, {
979
- authMode: "siwx",
980
- wallet,
981
- route: routeEntry.key
982
- });
1045
+ fireAuthVerified(ctx, { authMode: "siwx", wallet });
983
1046
  return runHandlerOnly(ctx, wallet, account);
984
1047
  }
985
1048
 
986
- // src/pipeline/context/should-parse-body-early.ts
1049
+ // src/pipeline/steps/should-parse-body-early.ts
987
1050
  function shouldParseBodyEarly(incomingStrategy, routeEntry, pricing) {
988
1051
  if (incomingStrategy) return false;
989
1052
  if (!routeEntry.bodySchema) return false;
990
1053
  return (pricing?.needsBody ?? false) || !!routeEntry.validateFn;
991
1054
  }
992
1055
 
993
- // src/pipeline/context/resolve-early-body.ts
1056
+ // src/pipeline/steps/resolve-early-body.ts
994
1057
  async function resolveEarlyBody(args) {
995
1058
  const { ctx, pricing, incomingStrategy } = args;
996
1059
  if (!shouldParseBodyEarly(incomingStrategy, ctx.routeEntry, pricing)) {
@@ -1022,24 +1085,19 @@ function extractBearerToken(header) {
1022
1085
  return null;
1023
1086
  }
1024
1087
 
1025
- // src/pipeline/context/run-api-key-gate.ts
1088
+ // src/pipeline/steps/run-api-key-gate.ts
1026
1089
  async function runApiKeyGate(ctx) {
1027
- const { request, routeEntry, deps } = ctx;
1090
+ const { request, routeEntry } = ctx;
1028
1091
  if (!routeEntry.apiKeyResolver) return { ok: true, account: void 0 };
1029
1092
  const apiKeyResult = await verifyApiKey(request, routeEntry.apiKeyResolver);
1030
1093
  if (!apiKeyResult.valid) {
1031
1094
  return { ok: false, response: fail(ctx, 401, "Invalid or missing API key") };
1032
1095
  }
1033
- firePluginHook(deps.plugin, "onAuthVerified", ctx.pluginCtx, {
1034
- authMode: "apiKey",
1035
- wallet: null,
1036
- route: routeEntry.key,
1037
- account: apiKeyResult.account
1038
- });
1096
+ fireAuthVerified(ctx, { authMode: "apiKey", wallet: null, account: apiKeyResult.account });
1039
1097
  return { ok: true, account: apiKeyResult.account };
1040
1098
  }
1041
1099
 
1042
- // src/pipeline/context/protocol-init-error.ts
1100
+ // src/pipeline/steps/protocol-init-error.ts
1043
1101
  function protocolInitError(routeEntry, deps) {
1044
1102
  if (!routeEntry.pricing) return null;
1045
1103
  const errors = [];
@@ -1062,15 +1120,65 @@ async function runApiKeyOnlyFlow(ctx) {
1062
1120
  }
1063
1121
  const result = await verifyApiKey(ctx.request, ctx.routeEntry.apiKeyResolver);
1064
1122
  if (!result.valid) return fail(ctx, 401, "Invalid or missing API key");
1065
- firePluginHook(ctx.deps.plugin, "onAuthVerified", ctx.pluginCtx, {
1066
- authMode: "apiKey",
1067
- wallet: null,
1068
- route: ctx.routeEntry.key,
1069
- account: result.account
1070
- });
1123
+ fireAuthVerified(ctx, { authMode: "apiKey", wallet: null, account: result.account });
1071
1124
  return runHandlerOnly(ctx, null, result.account);
1072
1125
  }
1073
1126
 
1127
+ // src/pricing/format.ts
1128
+ var USDC_DECIMALS = 6;
1129
+ var DECIMAL_RE = /^(\d+)(?:\.(\d+))?$/;
1130
+ function badDecimal(amount) {
1131
+ return Object.assign(new Error(`'${amount}' is not a valid decimal-dollar string`), {
1132
+ status: 400
1133
+ });
1134
+ }
1135
+ function decimalToAtomic(amount, decimals = USDC_DECIMALS) {
1136
+ const match = DECIMAL_RE.exec(amount.trim());
1137
+ if (!match) throw badDecimal(amount);
1138
+ const whole = match[1];
1139
+ const fraction = match[2] ?? "";
1140
+ if (fraction.length > decimals) {
1141
+ throw Object.assign(new Error(`Amount '${amount}' exceeds ${decimals} decimal places`), {
1142
+ status: 400
1143
+ });
1144
+ }
1145
+ const normalized = `${whole}${fraction.padEnd(decimals, "0")}`.replace(/^0+(?=\d)/, "");
1146
+ return BigInt(normalized || "0");
1147
+ }
1148
+ function atomicToDecimal(atomic, decimals = USDC_DECIMALS) {
1149
+ const divisor = 10n ** BigInt(decimals);
1150
+ const whole = atomic / divisor;
1151
+ const fraction = atomic % divisor;
1152
+ if (fraction === 0n) return whole.toString();
1153
+ const fractionStr = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
1154
+ return `${whole}.${fractionStr}`;
1155
+ }
1156
+ function compareDecimals(a, b) {
1157
+ const av = decimalToAtomic(a);
1158
+ const bv = decimalToAtomic(b);
1159
+ if (av < bv) return -1;
1160
+ if (av > bv) return 1;
1161
+ return 0;
1162
+ }
1163
+ function isPositiveDecimal(value) {
1164
+ try {
1165
+ return decimalToAtomic(value) > 0n;
1166
+ } catch {
1167
+ return false;
1168
+ }
1169
+ }
1170
+ function multiplyDecimal(decimal, factor) {
1171
+ if (!Number.isFinite(factor) || factor <= 0) return decimal;
1172
+ const [whole, fraction = ""] = decimal.split(".");
1173
+ const scaled = (BigInt(whole + fraction) * BigInt(factor)).toString();
1174
+ const decimals = fraction.length;
1175
+ if (decimals === 0) return scaled;
1176
+ const padded = scaled.padStart(decimals + 1, "0");
1177
+ const intPart = padded.slice(0, padded.length - decimals);
1178
+ const fracPart = padded.slice(padded.length - decimals).replace(/0+$/, "");
1179
+ return fracPart ? `${intPart}.${fracPart}` : intPart;
1180
+ }
1181
+
1074
1182
  // src/pricing/dynamic.ts
1075
1183
  var DynamicPricing = class {
1076
1184
  constructor(opts) {
@@ -1106,9 +1214,13 @@ var DynamicPricing = class {
1106
1214
  }
1107
1215
  cap(raw, body) {
1108
1216
  if (!this.opts.maxPrice) return raw;
1109
- const n = parseFloat(raw);
1110
- const max = parseFloat(this.opts.maxPrice);
1111
- if (!Number.isFinite(n) || n > max) {
1217
+ let overCap;
1218
+ try {
1219
+ overCap = compareDecimals(raw, this.opts.maxPrice) > 0;
1220
+ } catch {
1221
+ overCap = true;
1222
+ }
1223
+ if (overCap) {
1112
1224
  this.alert("warn", `Price ${raw} exceeds maxPrice ${this.opts.maxPrice}, capping`, {
1113
1225
  calculated: raw,
1114
1226
  maxPrice: this.opts.maxPrice,
@@ -1185,7 +1297,7 @@ var TieredPricing = class {
1185
1297
  maxTierPrice() {
1186
1298
  let max = "0";
1187
1299
  for (const tier of Object.values(this.opts.tiers)) {
1188
- if (parseFloat(tier.price) > parseFloat(max)) max = tier.price;
1300
+ if (compareDecimals(tier.price, max) > 0) max = tier.price;
1189
1301
  }
1190
1302
  return max;
1191
1303
  }
@@ -1663,17 +1775,6 @@ var mppStrategy = {
1663
1775
  return buildChargeChallenge(args);
1664
1776
  }
1665
1777
  };
1666
- function multiplyDecimal(decimal, factor) {
1667
- if (!Number.isFinite(factor) || factor <= 0) return decimal;
1668
- const [whole, fraction = ""] = decimal.split(".");
1669
- const scaled = (BigInt(whole + fraction) * BigInt(factor)).toString();
1670
- const decimals = fraction.length;
1671
- if (decimals === 0) return scaled;
1672
- const padded = scaled.padStart(decimals + 1, "0");
1673
- const intPart = padded.slice(0, padded.length - decimals);
1674
- const fracPart = padded.slice(padded.length - decimals).replace(/0+$/, "");
1675
- return fracPart ? `${intPart}.${fracPart}` : intPart;
1676
- }
1677
1778
  async function buildChargeChallenge(args) {
1678
1779
  if (!args.deps.mppx) return {};
1679
1780
  try {
@@ -1766,26 +1867,13 @@ function buildCustomRequirement(price, accept) {
1766
1867
  return {
1767
1868
  scheme: accept.scheme,
1768
1869
  network: accept.network,
1769
- amount: decimalToAtomicUnits(price, accept.decimals ?? 6),
1870
+ amount: decimalToAtomic(price, accept.decimals ?? 6).toString(),
1770
1871
  asset: accept.asset,
1771
1872
  payTo: accept.payTo,
1772
1873
  maxTimeoutSeconds: accept.maxTimeoutSeconds ?? 300,
1773
1874
  extra: accept.extra ?? {}
1774
1875
  };
1775
1876
  }
1776
- function decimalToAtomicUnits(amount, decimals) {
1777
- const match = /^(?<whole>\d+)(?:\.(?<fraction>\d+))?$/.exec(amount);
1778
- if (!match?.groups) {
1779
- throw new Error(`Invalid decimal amount '${amount}'`);
1780
- }
1781
- const whole = match.groups.whole;
1782
- const fraction = match.groups.fraction ?? "";
1783
- if (fraction.length > decimals) {
1784
- throw new Error(`Amount '${amount}' exceeds ${decimals} decimal places`);
1785
- }
1786
- const normalized = `${whole}${fraction.padEnd(decimals, "0")}`.replace(/^0+(?=\d)/, "");
1787
- return normalized === "" ? "0" : normalized;
1788
- }
1789
1877
 
1790
1878
  // src/protocols/x402/challenge.ts
1791
1879
  async function buildX402Challenge(opts) {
@@ -2148,28 +2236,14 @@ async function buildX402ChallengeContribution(args) {
2148
2236
  }
2149
2237
  function reportSettleFailure(report, err, network) {
2150
2238
  const facilitator = err ?? {};
2151
- report("error", "Settlement failed", {
2239
+ const meta = {
2152
2240
  error: err instanceof Error ? err.message : String(err),
2153
2241
  network,
2154
2242
  errorReason: facilitator.errorReason,
2155
2243
  facilitatorStatus: facilitator.response?.status,
2156
2244
  facilitatorBody: facilitator.response?.data ?? facilitator.response?.body
2157
- });
2158
- }
2159
-
2160
- // src/protocols/detect.ts
2161
- function detectProtocol(request) {
2162
- if (request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)) {
2163
- return "x402";
2164
- }
2165
- const auth = request.headers.get(HEADERS.AUTHORIZATION);
2166
- if (auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT)) {
2167
- return "mpp";
2168
- }
2169
- if (request.headers.get(HEADERS.SIWX)) {
2170
- return "siwx";
2171
- }
2172
- return null;
2245
+ };
2246
+ report("error", "Settlement failed", meta);
2173
2247
  }
2174
2248
 
2175
2249
  // src/protocols/index.ts
@@ -2192,13 +2266,14 @@ function getAllowedStrategies(allowed) {
2192
2266
  import { NextResponse as NextResponse4 } from "next/server";
2193
2267
 
2194
2268
  // src/pipeline/challenge-extensions.ts
2269
+ init_evm();
2195
2270
  async function buildChallengeExtensions(ctx) {
2196
2271
  const { routeEntry } = ctx;
2197
2272
  let extensions;
2198
2273
  try {
2199
- const { z } = await import("zod");
2274
+ const { z: z2 } = await import("zod");
2200
2275
  const { declareDiscoveryExtension } = await import("@x402/extensions/bazaar");
2201
- const toJSON = (schema) => z.toJSONSchema(schema, {
2276
+ const toJSON = (schema) => z2.toJSONSchema(schema, {
2202
2277
  target: "draft-2020-12",
2203
2278
  unrepresentable: "any"
2204
2279
  });
@@ -2219,11 +2294,10 @@ async function buildChallengeExtensions(ctx) {
2219
2294
  extensions = declareDiscoveryExtension(config);
2220
2295
  }
2221
2296
  } catch (err) {
2222
- firePluginHook(ctx.deps.plugin, "onAlert", ctx.pluginCtx, {
2223
- level: "warn",
2224
- message: `Bazaar schema generation failed: ${err instanceof Error ? err.message : String(err)}`,
2225
- route: routeEntry.key
2226
- });
2297
+ ctx.report(
2298
+ "warn",
2299
+ `Bazaar schema generation failed: ${err instanceof Error ? err.message : String(err)}`
2300
+ );
2227
2301
  }
2228
2302
  if (routeEntry.siwxEnabled) {
2229
2303
  try {
@@ -2237,7 +2311,24 @@ async function buildChallengeExtensions(ctx) {
2237
2311
  } catch {
2238
2312
  }
2239
2313
  }
2240
- return extensions;
2314
+ const hasEvmUpto = ctx.deps.x402Accepts.some(
2315
+ (accept) => accept.scheme === "upto" && isEvmNetwork(accept.network)
2316
+ );
2317
+ if (hasEvmUpto) {
2318
+ try {
2319
+ const { declareEip2612GasSponsoringExtension } = await import("@x402/extensions");
2320
+ extensions = {
2321
+ ...extensions ?? {},
2322
+ ...declareEip2612GasSponsoringExtension()
2323
+ };
2324
+ } catch (err) {
2325
+ ctx.report(
2326
+ "warn",
2327
+ `EIP-2612 gas-sponsoring declaration failed: ${err instanceof Error ? err.message : String(err)}`
2328
+ );
2329
+ }
2330
+ }
2331
+ return extensions;
2241
2332
  }
2242
2333
 
2243
2334
  // src/pipeline/flows/build402.ts
@@ -2357,7 +2448,7 @@ async function runDynamicChannelMgmtFlow(args) {
2357
2448
  return build402(ctx, pricing, parsedBody, verifyOutcome.failure);
2358
2449
  }
2359
2450
  ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
2360
- firePluginHook(deps.plugin, "onPaymentVerified", ctx.pluginCtx, {
2451
+ firePaymentVerified(ctx, {
2361
2452
  protocol: strategy.protocol,
2362
2453
  payer: verifyOutcome.wallet,
2363
2454
  amount: price,
@@ -2392,27 +2483,6 @@ async function runDynamicChannelMgmtFlow(args) {
2392
2483
  // src/pipeline/flows/dynamic/dynamic-invoke.ts
2393
2484
  import { NextResponse as NextResponse6 } from "next/server";
2394
2485
 
2395
- // src/pricing/atomic.ts
2396
- var USDC_DECIMALS = 6;
2397
- function decimalToAtomic(amount) {
2398
- const m = /^(\d+)(?:\.(\d+))?$/.exec(amount.trim());
2399
- if (!m) {
2400
- throw Object.assign(new Error(`'${amount}' is not a valid decimal-dollar string`), {
2401
- status: 400
2402
- });
2403
- }
2404
- const whole = m[1];
2405
- const fraction = (m[2] ?? "").slice(0, USDC_DECIMALS).padEnd(USDC_DECIMALS, "0");
2406
- return BigInt(`${whole}${fraction}`.replace(/^0+(?=\d)/, "") || "0");
2407
- }
2408
- function atomicToDecimal(atomic) {
2409
- const whole = atomic / 10n ** BigInt(USDC_DECIMALS);
2410
- const fraction = atomic % 10n ** BigInt(USDC_DECIMALS);
2411
- if (fraction === 0n) return whole.toString();
2412
- const fractionStr = fraction.toString().padStart(USDC_DECIMALS, "0").replace(/0+$/, "");
2413
- return `${whole}.${fractionStr}`;
2414
- }
2415
-
2416
2486
  // src/pricing/charge-context.ts
2417
2487
  function createChargeContext(args) {
2418
2488
  const { tickCost, maxPrice, route } = args;
@@ -2465,14 +2535,7 @@ async function invokeDynamic(ctx, wallet, account, body, payment) {
2465
2535
  wallet,
2466
2536
  payment,
2467
2537
  account,
2468
- alert(level, message, alertMeta) {
2469
- firePluginHook(ctx.deps.plugin, "onAlert", ctx.pluginCtx, {
2470
- level,
2471
- message,
2472
- route: ctx.routeEntry.key,
2473
- meta: alertMeta
2474
- });
2475
- },
2538
+ alert: ctx.report,
2476
2539
  setVerifiedWallet: (addr) => ctx.pluginCtx.setVerifiedWallet(addr)
2477
2540
  };
2478
2541
  const handlerCtx = chargeContext !== null ? { ...baseHandlerCtx, charge: chargeContext.charge } : baseHandlerCtx;
@@ -2537,7 +2600,7 @@ function resolveDynamicPreflight(strategy, request, routeEntry) {
2537
2600
  // src/pipeline/flows/dynamic/dynamic-request.ts
2538
2601
  async function runDynamicRequestFlow(args) {
2539
2602
  const { ctx, strategy, verifyOutcome, account, body, result } = args;
2540
- const { deps, routeEntry } = ctx;
2603
+ const { routeEntry } = ctx;
2541
2604
  const settleScope = {
2542
2605
  wallet: verifyOutcome.wallet,
2543
2606
  account,
@@ -2563,11 +2626,10 @@ async function runDynamicRequestFlow(args) {
2563
2626
  billedAmount,
2564
2627
  onSettleError: async (error, failMessage) => {
2565
2628
  await runSettlementError(ctx, settleScope, error, "settle");
2566
- firePluginHook(deps.plugin, "onAlert", ctx.pluginCtx, {
2567
- level: "critical",
2568
- message: `${strategy.protocol} ${failMessage}: ${errorMessage(error, "unknown")}`,
2569
- route: routeEntry.key
2570
- });
2629
+ ctx.report(
2630
+ "critical",
2631
+ `${strategy.protocol} ${failMessage}: ${errorMessage(error, "unknown")}`
2632
+ );
2571
2633
  }
2572
2634
  });
2573
2635
  }
@@ -2637,7 +2699,7 @@ async function runDynamicPaidFlow(ctx) {
2637
2699
  return build402(ctx, pricing, parsedBody, verifyOutcome.failure);
2638
2700
  }
2639
2701
  ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
2640
- firePluginHook(deps.plugin, "onPaymentVerified", ctx.pluginCtx, {
2702
+ firePaymentVerified(ctx, {
2641
2703
  protocol: incomingStrategy.protocol,
2642
2704
  payer: verifyOutcome.wallet,
2643
2705
  amount: price,
@@ -2703,7 +2765,6 @@ async function resolveStaticBodyAndPrice(args) {
2703
2765
  // src/pipeline/flows/static/static-request.ts
2704
2766
  async function runStaticRequestFlow(args) {
2705
2767
  const { ctx, strategy, verifyOutcome, account, body, price, result } = args;
2706
- const { deps, routeEntry } = ctx;
2707
2768
  const settleScope = {
2708
2769
  wallet: verifyOutcome.wallet,
2709
2770
  account,
@@ -2744,11 +2805,10 @@ async function runStaticRequestFlow(args) {
2744
2805
  billedAmount: price,
2745
2806
  onSettleError: async (error, failMessage) => {
2746
2807
  await runSettlementError(ctx, settleScope, error, "settle");
2747
- firePluginHook(deps.plugin, "onAlert", ctx.pluginCtx, {
2748
- level: "critical",
2749
- message: `${strategy.protocol} ${failMessage}: ${errorMessage(error, "unknown")}`,
2750
- route: routeEntry.key
2751
- });
2808
+ ctx.report(
2809
+ "critical",
2810
+ `${strategy.protocol} ${failMessage}: ${errorMessage(error, "unknown")}`
2811
+ );
2752
2812
  }
2753
2813
  });
2754
2814
  }
@@ -2794,7 +2854,7 @@ async function runStaticPaidFlow(ctx) {
2794
2854
  return build402(ctx, pricing, parsedBody, verifyOutcome.failure);
2795
2855
  }
2796
2856
  ctx.pluginCtx.setVerifiedWallet(verifyOutcome.wallet);
2797
- firePluginHook(deps.plugin, "onPaymentVerified", ctx.pluginCtx, {
2857
+ firePaymentVerified(ctx, {
2798
2858
  protocol: incomingStrategy.protocol,
2799
2859
  payer: verifyOutcome.wallet,
2800
2860
  amount: price,
@@ -2833,6 +2893,19 @@ async function runPaidFlow(ctx) {
2833
2893
  import { NextResponse as NextResponse7 } from "next/server";
2834
2894
 
2835
2895
  // src/kv-store/client.ts
2896
+ var BIGINT_SUFFIX = "#__bigint";
2897
+ function stringifyValue(value) {
2898
+ return JSON.stringify(
2899
+ value,
2900
+ (_key, v) => typeof v === "bigint" ? `${v.toString()}${BIGINT_SUFFIX}` : v
2901
+ );
2902
+ }
2903
+ function parseValue(raw) {
2904
+ return JSON.parse(
2905
+ raw,
2906
+ (_key, v) => typeof v === "string" && v.endsWith(BIGINT_SUFFIX) ? BigInt(v.slice(0, -BIGINT_SUFFIX.length)) : v
2907
+ );
2908
+ }
2836
2909
  function restKvStore(url, token) {
2837
2910
  const base = url.replace(/\/+$/, "");
2838
2911
  const authHeader = { Authorization: `Bearer ${token}` };
@@ -2854,16 +2927,22 @@ function restKvStore(url, token) {
2854
2927
  const res = await fetch(`${base}/get/${encodeURIComponent(key)}`, { headers: authHeader });
2855
2928
  if (!res.ok) throw new Error(`[kv-store] GET ${key}: ${res.status}`);
2856
2929
  const { result } = await res.json();
2857
- return result ?? null;
2930
+ if (result == null) return null;
2931
+ if (typeof result !== "string") return result;
2932
+ try {
2933
+ return parseValue(result);
2934
+ } catch {
2935
+ return result;
2936
+ }
2858
2937
  }
2859
2938
  async function set(key, value) {
2860
- await exec(["SET", key, JSON.stringify(value)]);
2939
+ await exec(["SET", key, stringifyValue(value)]);
2861
2940
  }
2862
2941
  async function del(key) {
2863
2942
  await exec(["DEL", key]);
2864
2943
  }
2865
2944
  async function setNxEx(key, value, ttlSeconds) {
2866
- const result = await exec(["SET", key, JSON.stringify(value), "EX", ttlSeconds, "NX"]);
2945
+ const result = await exec(["SET", key, stringifyValue(value), "EX", ttlSeconds, "NX"]);
2867
2946
  return result === "OK";
2868
2947
  }
2869
2948
  async function sadd(key, member) {
@@ -2978,6 +3057,21 @@ async function createKvMppStore(kv, options) {
2978
3057
  });
2979
3058
  }
2980
3059
 
3060
+ // src/protocols/detect.ts
3061
+ function detectProtocol(request) {
3062
+ if (request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)) {
3063
+ return "x402";
3064
+ }
3065
+ const auth = request.headers.get(HEADERS.AUTHORIZATION);
3066
+ if (auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT)) {
3067
+ return "mpp";
3068
+ }
3069
+ if (request.headers.get(HEADERS.SIWX)) {
3070
+ return "siwx";
3071
+ }
3072
+ return null;
3073
+ }
3074
+
2981
3075
  // src/protocols/mpp/siwx-mode.ts
2982
3076
  import { Credential as Credential2 } from "mppx";
2983
3077
  async function verifyMppSiwx(request, mppx) {
@@ -3017,11 +3111,7 @@ async function runSiwxOnlyFlow(ctx) {
3017
3111
  }
3018
3112
  if (mppSiwxResult.valid) {
3019
3113
  ctx.pluginCtx.setVerifiedWallet(mppSiwxResult.wallet);
3020
- firePluginHook(deps.plugin, "onAuthVerified", ctx.pluginCtx, {
3021
- authMode: "siwx",
3022
- wallet: mppSiwxResult.wallet,
3023
- route: routeEntry.key
3024
- });
3114
+ fireAuthVerified(ctx, { authMode: "siwx", wallet: mppSiwxResult.wallet });
3025
3115
  const authResponse = await runHandlerOnly(ctx, mppSiwxResult.wallet, void 0);
3026
3116
  if (authResponse.status < 400) {
3027
3117
  return mppSiwxResult.withReceipt(authResponse);
@@ -3043,11 +3133,7 @@ async function runSiwxOnlyFlow(ctx) {
3043
3133
  }
3044
3134
  const wallet = normalizeWalletAddress(siwx.wallet);
3045
3135
  ctx.pluginCtx.setVerifiedWallet(wallet);
3046
- firePluginHook(deps.plugin, "onAuthVerified", ctx.pluginCtx, {
3047
- authMode: "siwx",
3048
- wallet,
3049
- route: routeEntry.key
3050
- });
3136
+ fireAuthVerified(ctx, { authMode: "siwx", wallet });
3051
3137
  return runHandlerOnly(ctx, wallet, void 0);
3052
3138
  }
3053
3139
  async function buildSiwxChallenge(ctx) {
@@ -3140,7 +3226,7 @@ async function runUnprotectedFlow(ctx) {
3140
3226
  return runHandlerOnly(ctx, null, void 0);
3141
3227
  }
3142
3228
 
3143
- // src/orchestrate.ts
3229
+ // src/pipeline/orchestrate.ts
3144
3230
  function createRequestHandler(routeEntry, handler, deps) {
3145
3231
  return async (request) => {
3146
3232
  await deps.initPromise;
@@ -3185,142 +3271,109 @@ ${issues}`
3185
3271
  }
3186
3272
 
3187
3273
  // src/builder.ts
3188
- var RouteBuilder = class {
3189
- /** @internal */
3190
- _key;
3191
- /** @internal */
3192
- _registry;
3193
- /** @internal */
3194
- _deps;
3195
- /** @internal */
3196
- _authMode = null;
3197
- /** @internal */
3198
- _pricing;
3199
- /** @internal */
3200
- _siwxEnabled = false;
3201
- /** @internal */
3202
- _protocols = ["x402"];
3203
- /** @internal */
3204
- _maxPrice;
3205
- /** @internal */
3206
- _minPrice;
3207
- /** @internal */
3208
- _dynamicPrice = false;
3209
- /** @internal */
3210
- _tickCost;
3211
- /** @internal */
3212
- _unitType;
3213
- /** @internal */
3214
- _payTo;
3215
- /** @internal */
3216
- _bodySchema;
3217
- /** @internal */
3218
- _querySchema;
3219
- /** @internal */
3220
- _outputSchema;
3221
- /** @internal */
3222
- _inputExample = void 0;
3223
- /** @internal */
3224
- _hasInputExample = false;
3225
- /** @internal */
3226
- _outputExample = void 0;
3227
- /** @internal */
3228
- _hasOutputExample = false;
3229
- /** @internal */
3230
- _description;
3231
- /** @internal */
3232
- _path;
3233
- /** @internal */
3234
- _method = "POST";
3235
- /** @internal */
3236
- _apiKeyResolver;
3237
- /** @internal */
3238
- _providerName;
3239
- /** @internal */
3240
- _providerConfig;
3241
- /** @internal */
3242
- _validateFn;
3243
- /** @internal */
3244
- _settlement;
3245
- /** @internal */
3246
- _mppInfo;
3247
- constructor(key, registry, deps) {
3248
- this._key = key;
3249
- this._registry = registry;
3250
- this._deps = deps;
3274
+ var RouteBuilder = class _RouteBuilder {
3275
+ #s;
3276
+ constructor(key, registry, deps, defaults) {
3277
+ this.#s = {
3278
+ key,
3279
+ registry,
3280
+ deps,
3281
+ authMode: null,
3282
+ pricing: void 0,
3283
+ siwxEnabled: false,
3284
+ protocols: defaults?.protocols ? [...defaults.protocols] : ["x402"],
3285
+ maxPrice: void 0,
3286
+ minPrice: void 0,
3287
+ dynamicPrice: false,
3288
+ tickCost: void 0,
3289
+ unitType: void 0,
3290
+ payTo: void 0,
3291
+ bodySchema: void 0,
3292
+ querySchema: void 0,
3293
+ outputSchema: void 0,
3294
+ inputExample: void 0,
3295
+ hasInputExample: false,
3296
+ outputExample: void 0,
3297
+ hasOutputExample: false,
3298
+ description: void 0,
3299
+ path: void 0,
3300
+ method: "POST",
3301
+ apiKeyResolver: void 0,
3302
+ providerName: void 0,
3303
+ providerConfig: void 0,
3304
+ validateFn: void 0,
3305
+ settlement: void 0,
3306
+ mppInfo: void 0
3307
+ };
3251
3308
  }
3252
3309
  fork() {
3253
- const next = Object.create(Object.getPrototypeOf(this));
3254
- Object.assign(next, this);
3255
- next._protocols = [...this._protocols];
3310
+ const next = new _RouteBuilder(
3311
+ this.#s.key,
3312
+ this.#s.registry,
3313
+ this.#s.deps
3314
+ );
3315
+ next.#s = { ...this.#s, protocols: [...this.#s.protocols] };
3256
3316
  return next;
3257
3317
  }
3258
3318
  paid(pricingOrOptions, options) {
3259
- const { pricing, resolvedOptions } = resolvePaidArgs(this._key, pricingOrOptions, options);
3260
- if (this._authMode === "unprotected") {
3319
+ const { pricing, resolvedOptions } = resolvePaidArgs(this.#s.key, pricingOrOptions, options);
3320
+ if (this.#s.authMode === "unprotected") {
3261
3321
  throw new Error(
3262
- `route '${this._key}': Cannot combine .unprotected() and .paid() on the same route.`
3322
+ `route '${this.#s.key}': Cannot combine .unprotected() and .paid() on the same route.`
3263
3323
  );
3264
3324
  }
3265
- if (this._pricing !== void 0) {
3325
+ if (this.#s.pricing !== void 0) {
3266
3326
  throw new Error(
3267
- `route '${this._key}': Cannot call .paid() more than once on the same route.`
3327
+ `route '${this.#s.key}': Cannot call .paid() more than once on the same route.`
3268
3328
  );
3269
3329
  }
3270
3330
  const next = this.fork();
3271
- next._authMode = "paid";
3272
- next._pricing = pricing;
3331
+ next.#s.authMode = "paid";
3332
+ next.#s.pricing = pricing;
3273
3333
  if (resolvedOptions?.protocols) {
3274
- next._protocols = [...resolvedOptions.protocols];
3275
- } else if (next._protocols.length === 0) {
3276
- next._protocols = ["x402"];
3334
+ next.#s.protocols = [...resolvedOptions.protocols];
3335
+ } else if (next.#s.protocols.length === 0) {
3336
+ next.#s.protocols = ["x402"];
3277
3337
  }
3278
- if (resolvedOptions?.maxPrice) next._maxPrice = resolvedOptions.maxPrice;
3279
- if (resolvedOptions?.minPrice) next._minPrice = resolvedOptions.minPrice;
3280
- if (resolvedOptions?.payTo) next._payTo = resolvedOptions.payTo;
3281
- if (resolvedOptions?.mpp) next._mppInfo = resolvedOptions.mpp;
3282
- if (resolvedOptions?.dynamic) next._dynamicPrice = true;
3283
- if (resolvedOptions?.tickCost) next._tickCost = resolvedOptions.tickCost;
3284
- if (resolvedOptions?.unitType) next._unitType = resolvedOptions.unitType;
3338
+ if (resolvedOptions?.maxPrice) next.#s.maxPrice = resolvedOptions.maxPrice;
3339
+ if (resolvedOptions?.minPrice) next.#s.minPrice = resolvedOptions.minPrice;
3340
+ if (resolvedOptions?.payTo) next.#s.payTo = resolvedOptions.payTo;
3341
+ if (resolvedOptions?.mpp) next.#s.mppInfo = resolvedOptions.mpp;
3342
+ if (resolvedOptions?.dynamic) next.#s.dynamicPrice = true;
3343
+ if (resolvedOptions?.tickCost) next.#s.tickCost = resolvedOptions.tickCost;
3344
+ if (resolvedOptions?.unitType) next.#s.unitType = resolvedOptions.unitType;
3285
3345
  if (typeof pricing === "object" && "tiers" in pricing) {
3286
- if (next._dynamicPrice) {
3346
+ if (next.#s.dynamicPrice) {
3287
3347
  throw new Error(
3288
- `route '${this._key}': .paid({ dynamic: true }) is incompatible with tiered pricing`
3348
+ `route '${this.#s.key}': .paid({ dynamic: true }) is incompatible with tiered pricing`
3289
3349
  );
3290
3350
  }
3291
3351
  for (const [tierKey, tierConfig] of Object.entries(pricing.tiers)) {
3292
3352
  if (!tierKey) {
3293
- throw new Error(`route '${this._key}': tier key cannot be empty`);
3353
+ throw new Error(`route '${this.#s.key}': tier key cannot be empty`);
3294
3354
  }
3295
- const tierPrice = parseFloat(tierConfig.price);
3296
- if (isNaN(tierPrice) || tierPrice <= 0) {
3355
+ if (!isPositiveDecimal(tierConfig.price)) {
3297
3356
  throw new Error(
3298
- `route '${this._key}': tier '${tierKey}' price '${tierConfig.price}' must be a positive decimal string`
3357
+ `route '${this.#s.key}': tier '${tierKey}' price '${tierConfig.price}' must be a positive decimal string`
3299
3358
  );
3300
3359
  }
3301
3360
  }
3302
3361
  }
3303
- if (resolvedOptions?.maxPrice !== void 0) {
3304
- const parsed = parseFloat(resolvedOptions.maxPrice);
3305
- if (isNaN(parsed) || parsed <= 0) {
3306
- throw new Error(
3307
- `route '${this._key}': maxPrice '${resolvedOptions.maxPrice}' must be a positive decimal string`
3308
- );
3309
- }
3362
+ if (resolvedOptions?.maxPrice !== void 0 && !isPositiveDecimal(resolvedOptions.maxPrice)) {
3363
+ throw new Error(
3364
+ `route '${this.#s.key}': maxPrice '${resolvedOptions.maxPrice}' must be a positive decimal string`
3365
+ );
3310
3366
  }
3311
- if (resolvedOptions?.tickCost !== void 0) {
3312
- const parsed = parseFloat(resolvedOptions.tickCost);
3313
- if (isNaN(parsed) || parsed <= 0) {
3314
- throw new Error(
3315
- `route '${this._key}': tickCost '${resolvedOptions.tickCost}' must be a positive decimal string`
3316
- );
3317
- }
3367
+ if (resolvedOptions?.tickCost !== void 0 && !isPositiveDecimal(resolvedOptions.tickCost)) {
3368
+ throw new Error(
3369
+ `route '${this.#s.key}': tickCost '${resolvedOptions.tickCost}' must be a positive decimal string`
3370
+ );
3318
3371
  }
3319
- if (next._dynamicPrice && !next._maxPrice) {
3320
- throw new Error(`route '${this._key}': .paid({ dynamic: true }) requires maxPrice`);
3372
+ if (next.#s.dynamicPrice && !next.#s.maxPrice) {
3373
+ throw new Error(`route '${this.#s.key}': .paid({ dynamic: true }) requires maxPrice`);
3321
3374
  }
3322
- if (next._dynamicPrice && !next._tickCost) {
3323
- throw new Error(`route '${this._key}': .paid({ dynamic: true }) requires tickCost`);
3375
+ if (next.#s.dynamicPrice && !next.#s.tickCost) {
3376
+ throw new Error(`route '${this.#s.key}': .paid({ dynamic: true }) requires tickCost`);
3324
3377
  }
3325
3378
  return next;
3326
3379
  }
@@ -3335,25 +3388,25 @@ var RouteBuilder = class {
3335
3388
  * ```
3336
3389
  */
3337
3390
  siwx() {
3338
- if (this._authMode === "unprotected") {
3391
+ if (this.#s.authMode === "unprotected") {
3339
3392
  throw new Error(
3340
- `route '${this._key}': Cannot combine .unprotected() and .siwx() on the same route.`
3393
+ `route '${this.#s.key}': Cannot combine .unprotected() and .siwx() on the same route.`
3341
3394
  );
3342
3395
  }
3343
- if (this._apiKeyResolver) {
3396
+ if (this.#s.apiKeyResolver) {
3344
3397
  throw new Error(
3345
- `route '${this._key}': Combining .siwx() and .apiKey() is not supported on the same route.`
3398
+ `route '${this.#s.key}': Combining .siwx() and .apiKey() is not supported on the same route.`
3346
3399
  );
3347
3400
  }
3348
3401
  const next = this.fork();
3349
- next._siwxEnabled = true;
3350
- if (next._authMode === "paid" || next._pricing) {
3351
- next._authMode = "paid";
3352
- if (next._protocols.length === 0) next._protocols = ["x402"];
3402
+ next.#s.siwxEnabled = true;
3403
+ if (next.#s.authMode === "paid" || next.#s.pricing) {
3404
+ next.#s.authMode = "paid";
3405
+ if (next.#s.protocols.length === 0) next.#s.protocols = ["x402"];
3353
3406
  return next;
3354
3407
  }
3355
- next._authMode = "siwx";
3356
- next._protocols = [];
3408
+ next.#s.authMode = "siwx";
3409
+ next.#s.protocols = [];
3357
3410
  return next;
3358
3411
  }
3359
3412
  /**
@@ -3370,14 +3423,14 @@ var RouteBuilder = class {
3370
3423
  * ```
3371
3424
  */
3372
3425
  apiKey(resolver) {
3373
- if (this._siwxEnabled) {
3426
+ if (this.#s.siwxEnabled) {
3374
3427
  throw new Error(
3375
- `route '${this._key}': Combining .apiKey() and .siwx() is not supported on the same route.`
3428
+ `route '${this.#s.key}': Combining .apiKey() and .siwx() is not supported on the same route.`
3376
3429
  );
3377
3430
  }
3378
3431
  const next = this.fork();
3379
- next._authMode = "apiKey";
3380
- next._apiKeyResolver = resolver;
3432
+ next.#s.authMode = "apiKey";
3433
+ next.#s.apiKeyResolver = resolver;
3381
3434
  return next;
3382
3435
  }
3383
3436
  /**
@@ -3390,19 +3443,19 @@ var RouteBuilder = class {
3390
3443
  * ```
3391
3444
  */
3392
3445
  unprotected() {
3393
- if (this._authMode && this._authMode !== "unprotected") {
3446
+ if (this.#s.authMode && this.#s.authMode !== "unprotected") {
3394
3447
  throw new Error(
3395
- `route '${this._key}': Cannot combine .unprotected() and .${this._authMode}() on the same route.`
3448
+ `route '${this.#s.key}': Cannot combine .unprotected() and .${this.#s.authMode}() on the same route.`
3396
3449
  );
3397
3450
  }
3398
- if (this._pricing) {
3451
+ if (this.#s.pricing) {
3399
3452
  throw new Error(
3400
- `route '${this._key}': Cannot combine .unprotected() and .paid() on the same route.`
3453
+ `route '${this.#s.key}': Cannot combine .unprotected() and .paid() on the same route.`
3401
3454
  );
3402
3455
  }
3403
3456
  const next = this.fork();
3404
- next._authMode = "unprotected";
3405
- next._protocols = [];
3457
+ next.#s.authMode = "unprotected";
3458
+ next.#s.protocols = [];
3406
3459
  return next;
3407
3460
  }
3408
3461
  /**
@@ -3421,8 +3474,8 @@ var RouteBuilder = class {
3421
3474
  */
3422
3475
  provider(name, config) {
3423
3476
  const next = this.fork();
3424
- next._providerName = name;
3425
- next._providerConfig = config ?? {};
3477
+ next.#s.providerName = name;
3478
+ next.#s.providerConfig = config ?? {};
3426
3479
  return next;
3427
3480
  }
3428
3481
  /**
@@ -3437,7 +3490,7 @@ var RouteBuilder = class {
3437
3490
  */
3438
3491
  body(schema) {
3439
3492
  const next = this.fork();
3440
- next._bodySchema = schema;
3493
+ next.#s.bodySchema = schema;
3441
3494
  return next;
3442
3495
  }
3443
3496
  /**
@@ -3453,8 +3506,8 @@ var RouteBuilder = class {
3453
3506
  */
3454
3507
  query(schema) {
3455
3508
  const next = this.fork();
3456
- next._querySchema = schema;
3457
- next._method = "GET";
3509
+ next.#s.querySchema = schema;
3510
+ next.#s.method = "GET";
3458
3511
  return next;
3459
3512
  }
3460
3513
  /**
@@ -3471,7 +3524,7 @@ var RouteBuilder = class {
3471
3524
  */
3472
3525
  output(schema) {
3473
3526
  const next = this.fork();
3474
- next._outputSchema = schema;
3527
+ next.#s.outputSchema = schema;
3475
3528
  return next;
3476
3529
  }
3477
3530
  /**
@@ -3485,8 +3538,8 @@ var RouteBuilder = class {
3485
3538
  */
3486
3539
  inputExample(example) {
3487
3540
  const next = this.fork();
3488
- next._inputExample = example;
3489
- next._hasInputExample = true;
3541
+ next.#s.inputExample = example;
3542
+ next.#s.hasInputExample = true;
3490
3543
  return next;
3491
3544
  }
3492
3545
  /**
@@ -3500,8 +3553,8 @@ var RouteBuilder = class {
3500
3553
  */
3501
3554
  outputExample(example) {
3502
3555
  const next = this.fork();
3503
- next._outputExample = example;
3504
- next._hasOutputExample = true;
3556
+ next.#s.outputExample = example;
3557
+ next.#s.hasOutputExample = true;
3505
3558
  return next;
3506
3559
  }
3507
3560
  /**
@@ -3515,7 +3568,7 @@ var RouteBuilder = class {
3515
3568
  */
3516
3569
  description(text) {
3517
3570
  const next = this.fork();
3518
- next._description = text;
3571
+ next.#s.description = text;
3519
3572
  return next;
3520
3573
  }
3521
3574
  /**
@@ -3529,7 +3582,7 @@ var RouteBuilder = class {
3529
3582
  */
3530
3583
  path(p) {
3531
3584
  const next = this.fork();
3532
- next._path = p;
3585
+ next.#s.path = p;
3533
3586
  return next;
3534
3587
  }
3535
3588
  /**
@@ -3543,7 +3596,7 @@ var RouteBuilder = class {
3543
3596
  */
3544
3597
  method(m) {
3545
3598
  const next = this.fork();
3546
- next._method = m;
3599
+ next.#s.method = m;
3547
3600
  return next;
3548
3601
  }
3549
3602
  /**
@@ -3562,7 +3615,7 @@ var RouteBuilder = class {
3562
3615
  */
3563
3616
  validate(fn) {
3564
3617
  const next = this.fork();
3565
- next._validateFn = fn;
3618
+ next.#s.validateFn = fn;
3566
3619
  return next;
3567
3620
  }
3568
3621
  /**
@@ -3580,7 +3633,7 @@ var RouteBuilder = class {
3580
3633
  */
3581
3634
  settlement(lifecycle) {
3582
3635
  const next = this.fork();
3583
- next._settlement = lifecycle;
3636
+ next.#s.settlement = lifecycle;
3584
3637
  return next;
3585
3638
  }
3586
3639
  /**
@@ -3623,79 +3676,79 @@ var RouteBuilder = class {
3623
3676
  return this.register(fn, true);
3624
3677
  }
3625
3678
  register(handlerFn, streaming) {
3626
- if (!this._authMode) {
3679
+ if (!this.#s.authMode) {
3627
3680
  throw new Error(
3628
- `route '${this._key}': Select an auth mode: .paid(pricing), .siwx(), .apiKey(resolver), or .unprotected()`
3681
+ `route '${this.#s.key}': Select an auth mode: .paid(pricing), .siwx(), .apiKey(resolver), or .unprotected()`
3629
3682
  );
3630
3683
  }
3631
- if (this._validateFn && !this._bodySchema) {
3684
+ if (this.#s.validateFn && !this.#s.bodySchema) {
3632
3685
  throw new Error(
3633
- `route '${this._key}': .validate() requires .body() \u2014 validation runs on parsed body`
3686
+ `route '${this.#s.key}': .validate() requires .body() \u2014 validation runs on parsed body`
3634
3687
  );
3635
3688
  }
3636
- if (this._settlement && !this._pricing) {
3637
- throw new Error(`route '${this._key}': .settlement() requires a paid route`);
3689
+ if (this.#s.settlement && !this.#s.pricing) {
3690
+ throw new Error(`route '${this.#s.key}': .settlement() requires a paid route`);
3638
3691
  }
3639
- if (this._dynamicPrice && this._protocols.includes("x402")) {
3640
- const hasUpto = this._deps.x402Accepts.some((accept) => accept.scheme === "upto");
3692
+ if (this.#s.dynamicPrice && this.#s.protocols.includes("x402")) {
3693
+ const hasUpto = this.#s.deps.x402Accepts.some((accept) => accept.scheme === "upto");
3641
3694
  if (!hasUpto) {
3642
3695
  throw new Error(
3643
- `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.`
3696
+ `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.`
3644
3697
  );
3645
3698
  }
3646
3699
  }
3647
- if (this._dynamicPrice && this._protocols.includes("mpp")) {
3648
- if (!this._deps.mppSessionConfig) {
3700
+ if (this.#s.dynamicPrice && this.#s.protocols.includes("mpp")) {
3701
+ if (!this.#s.deps.mppSessionConfig) {
3649
3702
  throw new Error(
3650
- `route '${this._key}': .paid({ dynamic: true }) on an MPP route requires session mode. Set RouterConfig.mpp.session = {} and provide mpp.operatorKey.`
3703
+ `route '${this.#s.key}': .paid({ dynamic: true }) on an MPP route requires session mode. Set RouterConfig.mpp.session = {} and provide mpp.operatorKey.`
3651
3704
  );
3652
3705
  }
3653
3706
  }
3654
- if (streaming && !this._dynamicPrice) {
3707
+ if (streaming && !this.#s.dynamicPrice) {
3655
3708
  throw new Error(
3656
- `route '${this._key}': .stream() requires .paid({ dynamic: true }) \u2014 static/free routes can't meter per-chunk billing.`
3709
+ `route '${this.#s.key}': .stream() requires .paid({ dynamic: true }) \u2014 static/free routes can't meter per-chunk billing.`
3657
3710
  );
3658
3711
  }
3659
3712
  validateExamples(
3660
- this._key,
3661
- this._bodySchema,
3662
- this._querySchema,
3663
- this._outputSchema,
3664
- this._inputExample,
3665
- this._hasInputExample,
3666
- this._outputExample,
3667
- this._hasOutputExample
3713
+ this.#s.key,
3714
+ this.#s.bodySchema,
3715
+ this.#s.querySchema,
3716
+ this.#s.outputSchema,
3717
+ this.#s.inputExample,
3718
+ this.#s.hasInputExample,
3719
+ this.#s.outputExample,
3720
+ this.#s.hasOutputExample
3668
3721
  );
3669
3722
  const entry = {
3670
- key: this._key,
3671
- authMode: this._authMode,
3672
- siwxEnabled: this._siwxEnabled,
3673
- pricing: this._pricing,
3674
- dynamicPrice: this._dynamicPrice ? true : void 0,
3723
+ key: this.#s.key,
3724
+ authMode: this.#s.authMode,
3725
+ siwxEnabled: this.#s.siwxEnabled,
3726
+ pricing: this.#s.pricing,
3727
+ dynamicPrice: this.#s.dynamicPrice ? true : void 0,
3675
3728
  streaming: streaming ? true : void 0,
3676
- protocols: this._protocols,
3677
- bodySchema: this._bodySchema,
3678
- querySchema: this._querySchema,
3679
- outputSchema: this._outputSchema,
3680
- inputExample: this._hasInputExample ? this._inputExample : void 0,
3681
- outputExample: this._hasOutputExample ? this._outputExample : void 0,
3682
- description: this._description,
3683
- path: this._path,
3684
- method: this._method,
3685
- maxPrice: this._maxPrice,
3686
- minPrice: this._minPrice,
3687
- payTo: this._payTo,
3688
- apiKeyResolver: this._apiKeyResolver,
3689
- providerName: this._providerName,
3690
- providerConfig: this._providerConfig,
3691
- validateFn: this._validateFn,
3692
- settlement: this._settlement,
3693
- mppInfo: this._mppInfo,
3694
- tickCost: this._tickCost,
3695
- unitType: this._unitType
3729
+ protocols: this.#s.protocols,
3730
+ bodySchema: this.#s.bodySchema,
3731
+ querySchema: this.#s.querySchema,
3732
+ outputSchema: this.#s.outputSchema,
3733
+ inputExample: this.#s.hasInputExample ? this.#s.inputExample : void 0,
3734
+ outputExample: this.#s.hasOutputExample ? this.#s.outputExample : void 0,
3735
+ description: this.#s.description,
3736
+ path: this.#s.path,
3737
+ method: this.#s.method,
3738
+ maxPrice: this.#s.maxPrice,
3739
+ minPrice: this.#s.minPrice,
3740
+ payTo: this.#s.payTo,
3741
+ apiKeyResolver: this.#s.apiKeyResolver,
3742
+ providerName: this.#s.providerName,
3743
+ providerConfig: this.#s.providerConfig,
3744
+ validateFn: this.#s.validateFn,
3745
+ settlement: this.#s.settlement,
3746
+ mppInfo: this.#s.mppInfo,
3747
+ tickCost: this.#s.tickCost,
3748
+ unitType: this.#s.unitType
3696
3749
  };
3697
- this._registry.register(entry);
3698
- return createRequestHandler(entry, handlerFn, this._deps);
3750
+ this.#s.registry.register(entry);
3751
+ return createRequestHandler(entry, handlerFn, this.#s.deps);
3699
3752
  }
3700
3753
  };
3701
3754
  function resolvePaidArgs(routeKey, pricingOrOptions, options) {
@@ -3915,7 +3968,7 @@ function toProtocolObject(protocol, mppInfo) {
3915
3968
  mpp: {
3916
3969
  method: mppInfo?.method ?? "tempo",
3917
3970
  intent: mppInfo?.intent ?? "charge",
3918
- currency: mppInfo?.currency ?? TEMPO_USDC_CURRENCY
3971
+ currency: mppInfo?.currency ?? TEMPO_USDC_ADDRESS
3919
3972
  }
3920
3973
  };
3921
3974
  }
@@ -3939,17 +3992,16 @@ function buildPricingInfo(entry) {
3939
3992
  };
3940
3993
  }
3941
3994
  if ("tiers" in entry.pricing) {
3942
- const tierPrices = Object.values(entry.pricing.tiers).map((tier) => parseFloat(tier.price));
3943
- const min = Math.min(...tierPrices);
3944
- const max = Math.max(...tierPrices);
3945
- if (Number.isFinite(min) && Number.isFinite(max)) {
3946
- if (min === max) {
3995
+ const tierPrices = Object.values(entry.pricing.tiers).map((tier) => tier.price);
3996
+ const extrema = tierExtrema(tierPrices);
3997
+ if (extrema) {
3998
+ if (extrema.min === extrema.max) {
3947
3999
  return {
3948
- price: { mode: "fixed", currency: "USD", amount: String(min) }
4000
+ price: { mode: "fixed", currency: "USD", amount: extrema.min }
3949
4001
  };
3950
4002
  }
3951
4003
  return {
3952
- price: { mode: "dynamic", currency: "USD", min: String(min), max: String(max) }
4004
+ price: { mode: "dynamic", currency: "USD", min: extrema.min, max: extrema.max }
3953
4005
  };
3954
4006
  }
3955
4007
  return {
@@ -3963,6 +4015,20 @@ function buildPricingInfo(entry) {
3963
4015
  }
3964
4016
  return void 0;
3965
4017
  }
4018
+ function tierExtrema(prices) {
4019
+ if (prices.length === 0) return null;
4020
+ let min = prices[0];
4021
+ let max = prices[0];
4022
+ try {
4023
+ for (const price of prices.slice(1)) {
4024
+ if (compareDecimals(price, min) < 0) min = price;
4025
+ if (compareDecimals(price, max) > 0) max = price;
4026
+ }
4027
+ } catch {
4028
+ return null;
4029
+ }
4030
+ return { min, max };
4031
+ }
3966
4032
 
3967
4033
  // src/discovery/llms-txt.ts
3968
4034
  import { NextResponse as NextResponse10 } from "next/server";
@@ -3995,142 +4061,256 @@ function formatRouterConfigIssues(issues) {
3995
4061
  return issues.map((issue) => issue.message).join("\n");
3996
4062
  }
3997
4063
 
3998
- // src/config/validators/x402.ts
3999
- init_accepts();
4064
+ // src/config/schema.ts
4065
+ init_constants();
4066
+ import { z } from "zod";
4000
4067
 
4001
- // src/config/validators/shared.ts
4002
- init_evm();
4003
- init_solana();
4004
- init_accepts();
4005
- function isEvmAddress(value) {
4006
- return /^0x[a-fA-F0-9]{40}$/.test(value);
4007
- }
4008
- function isEvmPrivateKey(value) {
4009
- return /^0x[a-fA-F0-9]{64}$/.test(value);
4010
- }
4011
- function isSupportedX402Network(network) {
4012
- return isEvmNetwork(network) || isSolanaNetwork(network);
4013
- }
4014
- function findPlaceholderPayee(values) {
4015
- return values.find((value) => value !== void 0 && /^0x0{40}$/i.test(value)) ?? null;
4016
- }
4017
- function usesDefaultEvmFacilitator(config) {
4018
- return getConfiguredX402Networks(config).some(
4019
- (network) => typeof network === "string" && isEvmNetwork(network)
4020
- ) && config.x402?.facilitators?.evm === void 0;
4068
+ // src/config/utils.ts
4069
+ import { privateKeyToAccount } from "viem/accounts";
4070
+ var EVM_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
4071
+ var EVM_PRIVATE_KEY_RE = /^0x[a-fA-F0-9]{64}$/;
4072
+ var SOLANA_ADDRESS_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
4073
+ var ZERO_EVM_ADDRESS_RE = /^0x0{40}$/i;
4074
+ function isUrl(value) {
4075
+ try {
4076
+ new URL(value);
4077
+ return true;
4078
+ } catch {
4079
+ return false;
4080
+ }
4081
+ }
4082
+ var isEvmAddress = (v) => EVM_ADDRESS_RE.test(v);
4083
+ var isEvmPrivateKey = (v) => EVM_PRIVATE_KEY_RE.test(v);
4084
+ var isPlaceholderEvm = (v) => ZERO_EVM_ADDRESS_RE.test(v);
4085
+ var isSolanaAddress = (v) => SOLANA_ADDRESS_RE.test(v);
4086
+ var isX402Network = (v) => v.startsWith("eip155:") || v.startsWith("solana:");
4087
+ var canonicalizeEvm = (addr) => addr.toLowerCase();
4088
+ function operatorAddressesCollide(opKey, fpKey) {
4089
+ if (!opKey || !fpKey || !isEvmPrivateKey(opKey) || !isEvmPrivateKey(fpKey)) return null;
4090
+ const op = privateKeyToAccount(opKey).address.toLowerCase();
4091
+ const fp = privateKeyToAccount(fpKey).address.toLowerCase();
4092
+ return op === fp ? op : null;
4093
+ }
4094
+ function trimAll(raw) {
4095
+ const out = {};
4096
+ for (const [k, v] of Object.entries(raw)) {
4097
+ if (typeof v !== "string") {
4098
+ out[k] = void 0;
4099
+ continue;
4100
+ }
4101
+ const trimmed = v.trim();
4102
+ out[k] = trimmed.length > 0 ? trimmed : void 0;
4103
+ }
4104
+ return out;
4021
4105
  }
4022
4106
 
4023
- // src/config/validators/x402.ts
4024
- var CHECKS = [
4025
- checkAcceptNetworkPresent,
4026
- checkAcceptNetworkSupported,
4027
- checkNonExactRequiresAsset,
4028
- checkDecimalsAreValid,
4029
- checkPayee,
4030
- checkPlaceholderPayeeAddress,
4031
- checkCdpKeys
4032
- ];
4033
- function validateX402Config(config, env, options) {
4034
- const accepts = getConfiguredX402Accepts(config);
4035
- if (accepts.length === 0) {
4107
+ // src/config/schema.ts
4108
+ function addIssue(ctx, params, message, path = []) {
4109
+ ctx.addIssue({ code: "custom", path, params, message });
4110
+ }
4111
+ var x402 = { protocol: "x402" };
4112
+ var mpp = { protocol: "mpp" };
4113
+ var envShape = {
4114
+ BASE_URL: z.string().refine(isUrl, {
4115
+ params: { code: "invalid_base_url" },
4116
+ 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."
4117
+ }).optional(),
4118
+ EVM_PAYEE_ADDRESS: z.string().refine(isEvmAddress, {
4119
+ params: { code: "invalid_x402_payee", ...x402 },
4120
+ message: "EVM_PAYEE_ADDRESS must be a 0x-prefixed 20-byte EVM address \u2014 the wallet that receives x402 and MPP payments."
4121
+ }).refine((v) => !isPlaceholderEvm(v), {
4122
+ params: { code: "placeholder_payee", ...x402 },
4123
+ message: "EVM_PAYEE_ADDRESS is the zero address (0x000\u2026000) \u2014 payments to this address are unrecoverable. Set it to a wallet you control."
4124
+ }).optional(),
4125
+ CDP_API_KEY_ID: z.string().optional(),
4126
+ CDP_API_KEY_SECRET: z.string().optional(),
4127
+ SOLANA_PAYEE_ADDRESS: z.string().refine(isSolanaAddress, {
4128
+ params: { code: "invalid_solana_payee", ...x402 },
4129
+ message: "SOLANA_PAYEE_ADDRESS must be a base58 Solana address (32\u201344 chars). When set, the router also accepts Solana payments."
4130
+ }).optional(),
4131
+ SOLANA_FACILITATOR_URL: z.string().refine(isUrl, {
4132
+ params: { code: "invalid_solana_facilitator_url", ...x402 },
4133
+ message: "SOLANA_FACILITATOR_URL must be a valid URL \u2014 override for the Solana x402 facilitator. Defaults to DEFAULT_SOLANA_FACILITATOR_URL."
4134
+ }).optional(),
4135
+ MPP_SECRET_KEY: z.string().optional(),
4136
+ MPP_CURRENCY: z.string().refine(isEvmAddress, {
4137
+ params: { code: "invalid_mpp_currency", ...mpp },
4138
+ 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."
4139
+ }).optional(),
4140
+ TEMPO_RPC_URL: z.string().refine(isUrl, {
4141
+ params: { code: "invalid_mpp_rpc_url", ...mpp },
4142
+ message: "TEMPO_RPC_URL must be a valid URL \u2014 authenticated Tempo JSON-RPC endpoint. Public rpc.tempo.xyz returns 401."
4143
+ }).optional(),
4144
+ MPP_OPERATOR_KEY: z.string().refine(isEvmPrivateKey, {
4145
+ params: { code: "invalid_mpp_operator_key", ...mpp },
4146
+ 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."
4147
+ }).optional(),
4148
+ MPP_FEE_PAYER_KEY: z.string().refine(isEvmPrivateKey, {
4149
+ params: { code: "invalid_mpp_fee_payer_key", ...mpp },
4150
+ 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."
4151
+ }).optional(),
4152
+ KV_REST_API_URL: z.string().optional(),
4153
+ KV_REST_API_TOKEN: z.string().optional(),
4154
+ NODE_ENV: z.string().optional()
4155
+ };
4156
+ var ENV_KEYS = Object.keys(envShape);
4157
+ var EnvInputSchema = z.object(envShape).passthrough().superRefine((env, ctx) => {
4158
+ if (env.BASE_URL === void 0) {
4159
+ addIssue(
4160
+ ctx,
4161
+ { code: "missing_base_url" },
4162
+ "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.",
4163
+ ["BASE_URL"]
4164
+ );
4165
+ }
4166
+ if (env.EVM_PAYEE_ADDRESS === void 0) {
4167
+ addIssue(
4168
+ ctx,
4169
+ { code: "missing_x402_payee", ...x402 },
4170
+ "EVM_PAYEE_ADDRESS is required \u2014 the EVM address that receives x402 and MPP payments.",
4171
+ ["EVM_PAYEE_ADDRESS"]
4172
+ );
4173
+ }
4174
+ if (env.MPP_SECRET_KEY) {
4175
+ if (env.MPP_CURRENCY === void 0) {
4176
+ addIssue(
4177
+ ctx,
4178
+ { code: "missing_mpp_currency", ...mpp },
4179
+ "MPP_CURRENCY is required when MPP is enabled \u2014 the Tempo currency address MPP charges in. Use TEMPO_USDC_ADDRESS for Tempo USDC.",
4180
+ ["MPP_CURRENCY"]
4181
+ );
4182
+ }
4183
+ if (env.TEMPO_RPC_URL === void 0) {
4184
+ addIssue(
4185
+ ctx,
4186
+ { code: "missing_mpp_rpc_url", ...mpp },
4187
+ "TEMPO_RPC_URL is required when MPP is enabled \u2014 authenticated Tempo JSON-RPC endpoint. Public rpc.tempo.xyz returns 401.",
4188
+ ["TEMPO_RPC_URL"]
4189
+ );
4190
+ }
4191
+ }
4192
+ const collision = operatorAddressesCollide(env.MPP_OPERATOR_KEY, env.MPP_FEE_PAYER_KEY);
4193
+ if (collision) {
4194
+ addIssue(
4195
+ ctx,
4196
+ { code: "mpp_operator_equals_fee_payer", ...mpp },
4197
+ `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.`,
4198
+ ["MPP_FEE_PAYER_KEY"]
4199
+ );
4200
+ }
4201
+ });
4202
+ function collectKvWarnings(env, kvStoreOptionProvided) {
4203
+ if (kvStoreOptionProvided) return [];
4204
+ const warn = (code, message) => ({
4205
+ code,
4206
+ severity: "warning",
4207
+ message
4208
+ });
4209
+ if (env.KV_REST_API_URL && !env.KV_REST_API_TOKEN) {
4036
4210
  return [
4037
- {
4038
- code: "missing_x402_accepts",
4039
- protocol: "x402",
4040
- message: "x402 requires at least one accept configuration."
4041
- }
4211
+ warn(
4212
+ "kv_url_without_token",
4213
+ "KV_REST_API_URL is set but KV_REST_API_TOKEN is missing \u2014 falling back to in-memory KV (unsafe in serverless production)."
4214
+ )
4042
4215
  ];
4043
4216
  }
4044
- const args = { config, accepts, env, options };
4045
- return CHECKS.map((check) => check(args)).filter(
4046
- (issue) => issue !== null
4047
- );
4048
- }
4049
- function checkAcceptNetworkPresent({ accepts }) {
4050
- return accepts.some((accept) => !accept.network) ? { code: "missing_x402_network", protocol: "x402", message: "x402 accepts require a network." } : null;
4051
- }
4052
- function checkAcceptNetworkSupported({ accepts }) {
4053
- const unsupported = accepts.find(
4054
- (accept) => accept.network && !isSupportedX402Network(accept.network)
4055
- );
4056
- if (!unsupported) return null;
4057
- return {
4058
- code: "unsupported_x402_network",
4059
- protocol: "x402",
4060
- message: `unsupported x402 network '${unsupported.network}'. Use eip155:* or solana:*.`
4061
- };
4062
- }
4063
- function checkNonExactRequiresAsset({ accepts }) {
4064
- return accepts.some((accept) => (accept.scheme ?? "exact") !== "exact" && !accept.asset) ? {
4065
- code: "missing_x402_asset",
4066
- protocol: "x402",
4067
- message: "non-exact x402 accepts require an asset."
4068
- } : null;
4217
+ if (env.KV_REST_API_TOKEN && !env.KV_REST_API_URL) {
4218
+ return [
4219
+ warn(
4220
+ "kv_token_without_url",
4221
+ "KV_REST_API_TOKEN is set but KV_REST_API_URL is missing \u2014 falling back to in-memory KV (unsafe in serverless production)."
4222
+ )
4223
+ ];
4224
+ }
4225
+ if (env.KV_REST_API_URL && env.KV_REST_API_TOKEN && !isUrl(env.KV_REST_API_URL)) {
4226
+ return [
4227
+ warn(
4228
+ "invalid_kv_url",
4229
+ `KV_REST_API_URL is not a valid URL \u2014 KV calls will fail at request time. Got: ${env.KV_REST_API_URL}`
4230
+ )
4231
+ ];
4232
+ }
4233
+ if (!env.KV_REST_API_URL && !env.KV_REST_API_TOKEN && env.NODE_ENV === "production") {
4234
+ return [
4235
+ warn(
4236
+ "missing_kv_in_production",
4237
+ "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."
4238
+ )
4239
+ ];
4240
+ }
4241
+ return [];
4069
4242
  }
4070
- function checkDecimalsAreValid({ accepts }) {
4071
- const invalid = accepts.find(
4072
- (accept) => accept.decimals !== void 0 && (!Number.isInteger(accept.decimals) || accept.decimals < 0)
4073
- );
4074
- if (!invalid) return null;
4075
- return {
4076
- code: "invalid_x402_decimals",
4077
- protocol: "x402",
4078
- message: "x402 accept decimals must be a non-negative integer."
4079
- };
4243
+ function getConfiguredX402Accepts2(config) {
4244
+ if (config.x402?.accepts?.length) return [...config.x402.accepts];
4245
+ return [
4246
+ {
4247
+ scheme: "exact",
4248
+ network: config.network ?? BASE_MAINNET_NETWORK,
4249
+ payTo: config.payeeAddress
4250
+ }
4251
+ ];
4080
4252
  }
4081
- function checkPayee({ config, accepts }) {
4082
- if (config.payeeAddress) return null;
4083
- return accepts.some((accept) => !accept.payTo) ? {
4084
- code: "missing_x402_payee",
4085
- protocol: "x402",
4086
- message: "x402 requires payeeAddress in router config or payTo on every x402 accept."
4087
- } : null;
4088
- }
4089
- function checkPlaceholderPayeeAddress({
4090
- config,
4091
- accepts
4092
- }) {
4093
- const placeholder = findPlaceholderPayee([
4253
+ function validateX402Config(config, env, options) {
4254
+ const accepts = getConfiguredX402Accepts2(config);
4255
+ const issues = [];
4256
+ const push = (code, message) => issues.push({ code, protocol: "x402", message });
4257
+ if (accepts.length === 0) {
4258
+ push("missing_x402_accepts", "x402 requires at least one accept configuration.");
4259
+ return issues;
4260
+ }
4261
+ if (accepts.some((a) => !a.network)) {
4262
+ push("missing_x402_network", "x402 accepts require a network.");
4263
+ }
4264
+ const unsupported = accepts.find((a) => a.network && !isX402Network(a.network));
4265
+ if (unsupported) {
4266
+ push(
4267
+ "unsupported_x402_network",
4268
+ `unsupported x402 network '${unsupported.network}'. Use eip155:* or solana:*.`
4269
+ );
4270
+ }
4271
+ if (accepts.some((a) => (a.scheme ?? "exact") !== "exact" && !a.asset)) {
4272
+ push("missing_x402_asset", "non-exact x402 accepts require an asset.");
4273
+ }
4274
+ if (accepts.some(
4275
+ (a) => a.decimals !== void 0 && (!Number.isInteger(a.decimals) || a.decimals < 0)
4276
+ )) {
4277
+ push("invalid_x402_decimals", "x402 accept decimals must be a non-negative integer.");
4278
+ }
4279
+ if (!config.payeeAddress && accepts.some((a) => !a.payTo)) {
4280
+ push(
4281
+ "missing_x402_payee",
4282
+ "x402 requires payeeAddress in router config or payTo on every x402 accept."
4283
+ );
4284
+ }
4285
+ const placeholder = [
4094
4286
  config.payeeAddress,
4095
- ...accepts.map((accept) => typeof accept.payTo === "string" ? accept.payTo : void 0)
4096
- ]);
4097
- if (!placeholder) return null;
4098
- return {
4099
- code: "placeholder_payee",
4100
- protocol: "x402",
4101
- message: `x402 payee '${placeholder}' is a placeholder address and cannot receive payments.`
4102
- };
4103
- }
4104
- function checkCdpKeys({ config, env, options }) {
4105
- if (options.requireCdpKeys === false) return null;
4106
- if (!usesDefaultEvmFacilitator(config)) return null;
4107
- const missing = [
4108
- env.CDP_API_KEY_ID ? null : "CDP_API_KEY_ID",
4109
- env.CDP_API_KEY_SECRET ? null : "CDP_API_KEY_SECRET"
4110
- ].filter(Boolean);
4111
- if (missing.length === 0) return null;
4112
- return {
4113
- code: "missing_cdp_keys",
4114
- protocol: "x402",
4115
- message: `default EVM x402 facilitator requires ${missing.join(" and ")}.`
4116
- };
4287
+ ...accepts.map((a) => typeof a.payTo === "string" ? a.payTo : void 0)
4288
+ ].find((v) => v !== void 0 && isPlaceholderEvm(v));
4289
+ if (placeholder) {
4290
+ push(
4291
+ "placeholder_payee",
4292
+ `x402 payee '${placeholder}' is a placeholder address and cannot receive payments.`
4293
+ );
4294
+ }
4295
+ if (options.requireCdpKeys !== false) {
4296
+ const hasEvm = accepts.some(
4297
+ (a) => typeof a.network === "string" && a.network.startsWith("eip155:")
4298
+ );
4299
+ if (hasEvm) {
4300
+ const missing = ["CDP_API_KEY_ID", "CDP_API_KEY_SECRET"].filter((k) => !env[k]);
4301
+ if (missing.length > 0) {
4302
+ push(
4303
+ "missing_cdp_keys",
4304
+ `x402 EVM facilitator (Coinbase) requires ${missing.join(" and ")}.`
4305
+ );
4306
+ }
4307
+ }
4308
+ }
4309
+ return issues;
4117
4310
  }
4118
-
4119
- // src/config/validators/mpp.ts
4120
- import { privateKeyToAccount } from "viem/accounts";
4121
- var CHECKS2 = [
4122
- checkSecretKey,
4123
- checkCurrency,
4124
- checkRecipient,
4125
- checkPlaceholderRecipient,
4126
- checkRpcUrl,
4127
- checkFeePayerKey,
4128
- checkOperatorKey,
4129
- checkOperatorMatchesFeePayer
4130
- ];
4131
- function validateMppConfig(config, env) {
4132
- const mpp = config.mpp;
4133
- if (!mpp) {
4311
+ function validateMppConfig(config) {
4312
+ const m = config.mpp;
4313
+ if (!m) {
4134
4314
  return [
4135
4315
  {
4136
4316
  code: "missing_mpp_config",
@@ -4139,109 +4319,184 @@ function validateMppConfig(config, env) {
4139
4319
  }
4140
4320
  ];
4141
4321
  }
4142
- const args = { config, mpp, env };
4143
- return CHECKS2.map((check) => check(args)).filter(
4144
- (issue) => issue !== null
4322
+ const issues = [];
4323
+ const push = (code, message) => issues.push({ code, protocol: "mpp", message });
4324
+ if (!m.secretKey) {
4325
+ push(
4326
+ "missing_mpp_secret_key",
4327
+ "MPP requires secretKey. Set MPP_SECRET_KEY or pass mpp.secretKey."
4328
+ );
4329
+ }
4330
+ if (!m.currency) {
4331
+ push("missing_mpp_currency", "MPP requires currency. Set MPP_CURRENCY or pass mpp.currency.");
4332
+ } else if (!isEvmAddress(m.currency)) {
4333
+ push(
4334
+ "invalid_mpp_currency",
4335
+ "MPP currency must be a 0x-prefixed 20-byte Tempo currency address. Use TEMPO_USDC_ADDRESS for Tempo USDC."
4336
+ );
4337
+ }
4338
+ const recipient = m.recipient ?? config.payeeAddress;
4339
+ if (!recipient) {
4340
+ push(
4341
+ "missing_mpp_recipient",
4342
+ "MPP requires a recipient address. Set mpp.recipient or payeeAddress in your router config."
4343
+ );
4344
+ } else if (!isEvmAddress(recipient)) {
4345
+ push(
4346
+ "invalid_mpp_recipient",
4347
+ "MPP recipient must be a 0x-prefixed EVM address. Solana recipients require x402."
4348
+ );
4349
+ }
4350
+ const placeholder = [m.recipient, config.payeeAddress].find(
4351
+ (v) => typeof v === "string" && isPlaceholderEvm(v)
4145
4352
  );
4146
- }
4147
- function checkSecretKey({ mpp }) {
4148
- if (mpp.secretKey) return null;
4149
- return {
4150
- code: "missing_mpp_secret_key",
4151
- protocol: "mpp",
4152
- message: "MPP requires secretKey. Set MPP_SECRET_KEY or pass mpp.secretKey."
4153
- };
4154
- }
4155
- function checkCurrency({ mpp }) {
4156
- if (!mpp.currency) {
4157
- return {
4158
- code: "missing_mpp_currency",
4159
- protocol: "mpp",
4160
- message: "MPP requires currency. Set MPP_CURRENCY or pass mpp.currency."
4161
- };
4353
+ if (placeholder) {
4354
+ push(
4355
+ "placeholder_payee",
4356
+ `MPP recipient '${placeholder}' is a placeholder address and cannot receive payments.`
4357
+ );
4162
4358
  }
4163
- if (!isEvmAddress(mpp.currency)) {
4164
- return {
4165
- code: "invalid_mpp_currency",
4166
- protocol: "mpp",
4167
- message: "MPP currency must be a 0x-prefixed 20-byte Tempo currency address. Use TEMPO_USDC_CURRENCY for Tempo USDC."
4168
- };
4359
+ if (!m.rpcUrl) {
4360
+ push(
4361
+ "missing_mpp_rpc_url",
4362
+ "MPP requires an authenticated Tempo RPC URL. Set TEMPO_RPC_URL env var or pass rpcUrl in the mpp config object."
4363
+ );
4169
4364
  }
4170
- return null;
4365
+ if (m.feePayerKey && !isEvmPrivateKey(m.feePayerKey)) {
4366
+ push(
4367
+ "invalid_mpp_fee_payer_key",
4368
+ "MPP feePayerKey must be a 0x-prefixed 32-byte EVM private key."
4369
+ );
4370
+ }
4371
+ if (m.operatorKey && !isEvmPrivateKey(m.operatorKey)) {
4372
+ push(
4373
+ "invalid_mpp_operator_key",
4374
+ "MPP operatorKey must be a 0x-prefixed 32-byte EVM private key."
4375
+ );
4376
+ }
4377
+ const collision = operatorAddressesCollide(m.operatorKey, m.feePayerKey);
4378
+ if (collision) {
4379
+ push(
4380
+ "mpp_operator_equals_fee_payer",
4381
+ `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).`
4382
+ );
4383
+ }
4384
+ return issues;
4171
4385
  }
4172
- function checkRecipient({ config, mpp }) {
4173
- const recipient = mpp.recipient ?? config.payeeAddress;
4174
- if (!recipient) {
4386
+ function translateZodIssues(error) {
4387
+ return error.issues.map((issue) => {
4388
+ const params = issue.params;
4389
+ if (!params?.code) {
4390
+ throw new Error(
4391
+ `[router] schema issue missing params.code (path=${issue.path.join(".")}, message=${issue.message}). Every refinement / addIssue call must set params.code.`
4392
+ );
4393
+ }
4175
4394
  return {
4176
- code: "missing_mpp_recipient",
4177
- protocol: "mpp",
4178
- message: "MPP requires a recipient address. Set mpp.recipient or payeeAddress in your router config."
4395
+ code: params.code,
4396
+ message: issue.message,
4397
+ ...params.protocol ? { protocol: params.protocol } : {},
4398
+ ...params.severity ? { severity: params.severity } : {}
4179
4399
  };
4400
+ });
4401
+ }
4402
+ function routerConfigFromEnv(options) {
4403
+ const rawEnv = options.env ?? process.env;
4404
+ const env = trimAll(rawEnv);
4405
+ const optionIssues = [];
4406
+ if (!options.title?.trim()) {
4407
+ optionIssues.push({
4408
+ code: "missing_discovery_title",
4409
+ message: "discovery `title` is required. Pass a short product name."
4410
+ });
4180
4411
  }
4181
- if (!isEvmAddress(recipient)) {
4182
- return {
4183
- code: "invalid_mpp_recipient",
4184
- protocol: "mpp",
4185
- message: "MPP recipient must be a 0x-prefixed EVM address. Solana recipients require x402."
4186
- };
4412
+ if (!options.description?.trim()) {
4413
+ optionIssues.push({
4414
+ code: "missing_discovery_description",
4415
+ message: "discovery `description` is required. One sentence is enough."
4416
+ });
4187
4417
  }
4188
- return null;
4189
- }
4190
- function checkPlaceholderRecipient({ config, mpp }) {
4191
- const placeholder = findPlaceholderPayee([mpp.recipient, config.payeeAddress]);
4192
- if (!placeholder) return null;
4193
- return {
4194
- code: "placeholder_payee",
4195
- protocol: "mpp",
4196
- message: `MPP recipient '${placeholder}' is a placeholder address and cannot receive payments.`
4197
- };
4198
- }
4199
- function checkRpcUrl({ mpp, env }) {
4200
- if (mpp.rpcUrl ?? env.TEMPO_RPC_URL) return null;
4201
- return {
4202
- code: "missing_mpp_rpc_url",
4203
- protocol: "mpp",
4204
- message: "MPP requires an authenticated Tempo RPC URL. Set TEMPO_RPC_URL env var or pass rpcUrl in the mpp config object."
4205
- };
4206
- }
4207
- function checkFeePayerKey({ mpp }) {
4208
- if (!mpp.feePayerKey || isEvmPrivateKey(mpp.feePayerKey)) return null;
4209
- return {
4210
- code: "invalid_mpp_fee_payer_key",
4211
- protocol: "mpp",
4212
- message: "MPP feePayerKey must be a 0x-prefixed 32-byte EVM private key."
4213
- };
4214
- }
4215
- function checkOperatorKey({ mpp }) {
4216
- if (!mpp.operatorKey || isEvmPrivateKey(mpp.operatorKey)) return null;
4217
- return {
4218
- code: "invalid_mpp_operator_key",
4219
- protocol: "mpp",
4220
- message: "MPP operatorKey must be a 0x-prefixed 32-byte EVM private key."
4221
- };
4222
- }
4223
- function checkOperatorMatchesFeePayer({ mpp }) {
4224
- if (!mpp.operatorKey || !mpp.feePayerKey) return null;
4225
- if (!isEvmPrivateKey(mpp.operatorKey) || !isEvmPrivateKey(mpp.feePayerKey)) return null;
4226
- const opAddr = privateKeyToAccount(mpp.operatorKey).address.toLowerCase();
4227
- const fpAddr = privateKeyToAccount(mpp.feePayerKey).address.toLowerCase();
4228
- if (opAddr !== fpAddr) return null;
4418
+ if (options.guidance === void 0) {
4419
+ optionIssues.push({
4420
+ code: "missing_discovery_guidance",
4421
+ message: "discovery `guidance` is required. Provide an empty string to opt out of `/llms.txt`."
4422
+ });
4423
+ }
4424
+ if (options.serverUrl !== void 0 && !isUrl(options.serverUrl)) {
4425
+ optionIssues.push({
4426
+ code: "invalid_server_url",
4427
+ message: `discovery \`serverUrl\` must be a valid URL. Got: ${options.serverUrl}`
4428
+ });
4429
+ }
4430
+ const parsed = EnvInputSchema.safeParse(env);
4431
+ const envIssues = parsed.success ? [] : translateZodIssues(parsed.error);
4432
+ const issues = [...envIssues, ...optionIssues];
4433
+ if (issues.length > 0) throw new RouterConfigError(issues);
4434
+ for (const warning of collectKvWarnings(env, options.kvStore !== void 0)) {
4435
+ console.warn(`[router] ${warning.message}`);
4436
+ }
4437
+ const payeeAddress = canonicalizeEvm(env.EVM_PAYEE_ADDRESS);
4438
+ const accepts = [
4439
+ { scheme: "exact", network: BASE_MAINNET_NETWORK, payTo: payeeAddress },
4440
+ {
4441
+ scheme: "upto",
4442
+ network: BASE_MAINNET_NETWORK,
4443
+ payTo: payeeAddress,
4444
+ asset: BASE_USDC_ADDRESS,
4445
+ decimals: BASE_USDC_DECIMALS
4446
+ }
4447
+ ];
4448
+ if (env.SOLANA_PAYEE_ADDRESS) {
4449
+ accepts.push({
4450
+ scheme: "exact",
4451
+ network: SOLANA_MAINNET_NETWORK,
4452
+ payTo: env.SOLANA_PAYEE_ADDRESS
4453
+ });
4454
+ }
4455
+ const configuredSolanaFacilitator = options.x402Facilitators?.solana;
4456
+ const solanaFacilitator = typeof configuredSolanaFacilitator === "string" ? configuredSolanaFacilitator : configuredSolanaFacilitator ?? env.SOLANA_FACILITATOR_URL ?? DEFAULT_SOLANA_FACILITATOR_URL;
4457
+ const mppEnabled = options.protocols?.includes("mpp") ?? Boolean(env.MPP_SECRET_KEY);
4458
+ const protocols = options.protocols ? [...options.protocols] : mppEnabled ? ["x402", "mpp"] : ["x402"];
4459
+ const mppConfig = mppEnabled ? {
4460
+ secretKey: env.MPP_SECRET_KEY,
4461
+ currency: canonicalizeEvm(env.MPP_CURRENCY),
4462
+ rpcUrl: env.TEMPO_RPC_URL,
4463
+ recipient: payeeAddress,
4464
+ ...env.MPP_FEE_PAYER_KEY ? { feePayerKey: env.MPP_FEE_PAYER_KEY } : {},
4465
+ ...env.MPP_OPERATOR_KEY ? { operatorKey: env.MPP_OPERATOR_KEY, session: {} } : {}
4466
+ } : void 0;
4229
4467
  return {
4230
- code: "mpp_operator_equals_fee_payer",
4231
- protocol: "mpp",
4232
- 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).`
4468
+ payeeAddress,
4469
+ baseUrl: env.BASE_URL,
4470
+ network: BASE_MAINNET_NETWORK,
4471
+ protocols,
4472
+ x402: {
4473
+ accepts,
4474
+ facilitators: {
4475
+ ...options.x402Facilitators,
4476
+ solana: solanaFacilitator
4477
+ }
4478
+ },
4479
+ ...mppConfig ? { mpp: mppConfig } : {},
4480
+ discovery: {
4481
+ title: options.title,
4482
+ version: options.version ?? "1.0.0",
4483
+ description: options.description,
4484
+ guidance: options.guidance,
4485
+ ...options.contact ? { contact: options.contact } : {},
4486
+ ...options.ownershipProofs ? { ownershipProofs: options.ownershipProofs } : {},
4487
+ ...options.methodHints ? { methodHints: options.methodHints } : {},
4488
+ ...options.serverUrl ? { serverUrl: options.serverUrl } : {}
4489
+ },
4490
+ ...options.prices ? { prices: options.prices } : {},
4491
+ ...options.plugin ? { plugin: options.plugin } : {},
4492
+ ...options.kvStore ? { kvStore: options.kvStore } : {},
4493
+ strictRoutes: options.strictRoutes ?? false
4233
4494
  };
4234
4495
  }
4235
-
4236
- // src/config/validate.ts
4237
- function validateRouterConfig(config, options = {}) {
4238
- const issues = getRouterConfigIssues(config, options);
4239
- if (issues.length > 0) throw new RouterConfigError(issues);
4240
- }
4241
4496
  function getRouterConfigIssues(config, options = {}) {
4242
- const env = options.env ?? process.env;
4243
- const issues = [];
4497
+ const env = options.env ?? {};
4244
4498
  const protocols = config.protocols ?? ["x402"];
4499
+ const issues = [];
4245
4500
  if (!config.baseUrl) {
4246
4501
  issues.push({
4247
4502
  code: "missing_base_url",
@@ -4254,84 +4509,17 @@ function getRouterConfigIssues(config, options = {}) {
4254
4509
  message: "RouterConfig.protocols cannot be empty. Omit the field to use default ['x402'] or specify protocols explicitly."
4255
4510
  });
4256
4511
  }
4257
- if (protocols.includes("x402")) {
4258
- issues.push(...validateX402Config(config, env, options));
4259
- }
4260
- if (protocols.includes("mpp")) {
4261
- issues.push(...validateMppConfig(config, env));
4262
- }
4512
+ if (protocols.includes("x402")) issues.push(...validateX402Config(config, env, options));
4513
+ if (protocols.includes("mpp")) issues.push(...validateMppConfig(config));
4263
4514
  return issues;
4264
4515
  }
4265
4516
 
4266
- // src/config/env.ts
4267
- init_constants();
4268
- function mppFromEnv(env, options = {}) {
4269
- const secretKey = env.MPP_SECRET_KEY;
4270
- const currency = env.MPP_CURRENCY;
4271
- const rpcUrl = env.TEMPO_RPC_URL;
4272
- const feePayerKey = options.feePayerKey ?? env.MPP_FEE_PAYER_KEY;
4273
- const feePayerKeySource = options.feePayerKey !== void 0 ? "feePayerKey" : "MPP_FEE_PAYER_KEY";
4274
- const hasAnyMppEnv = Boolean(secretKey || currency || rpcUrl || options.require);
4275
- if (!hasAnyMppEnv) return void 0;
4276
- const missing = [
4277
- secretKey ? null : "MPP_SECRET_KEY",
4278
- currency ? null : "MPP_CURRENCY",
4279
- rpcUrl ? null : "TEMPO_RPC_URL"
4280
- ].filter(Boolean);
4281
- if (missing.length > 0) {
4282
- throw new Error(`MPP env is incomplete. Missing: ${missing.join(", ")}`);
4283
- }
4284
- if (!isEvmAddress(currency)) {
4285
- throw new Error("MPP_CURRENCY must be a 0x-prefixed 20-byte Tempo currency address");
4286
- }
4287
- if (options.recipient && !isEvmAddress(options.recipient)) {
4288
- throw new Error("MPP recipient must be a 0x-prefixed EVM address");
4289
- }
4290
- if (feePayerKey && !isEvmPrivateKey(feePayerKey)) {
4291
- throw new Error(`${feePayerKeySource} must be a 0x-prefixed 32-byte EVM private key`);
4292
- }
4293
- return {
4294
- secretKey,
4295
- currency,
4296
- rpcUrl,
4297
- ...options.recipient ? { recipient: options.recipient } : {},
4298
- ...feePayerKey ? { feePayerKey } : {}
4299
- };
4300
- }
4301
- function x402AcceptsFromEnv(env, options = {}) {
4302
- const payeeEnv = options.payeeEnv ?? "X402_WALLET_ADDRESS";
4303
- const solanaPayeeEnv = options.solanaPayeeEnv ?? "SOLANA_PAYEE_ADDRESS";
4304
- const payeeAddress = options.payeeAddress ?? env[payeeEnv];
4305
- if (!payeeAddress) {
4306
- throw new Error(`${payeeEnv} is required to build x402 accepts`);
4307
- }
4308
- const accepts = [
4309
- {
4310
- scheme: "exact",
4311
- network: options.network ?? BASE_NETWORK,
4312
- payTo: payeeAddress
4313
- }
4314
- ];
4315
- const solanaPayeeAddress = options.solanaPayeeAddress ?? env[solanaPayeeEnv];
4316
- if (solanaPayeeAddress) {
4317
- accepts.push({
4318
- scheme: "exact",
4319
- network: SOLANA_MAINNET_NETWORK,
4320
- payTo: solanaPayeeAddress
4321
- });
4322
- }
4323
- return accepts;
4324
- }
4325
- function paidOptionsForProtocols(protocols) {
4326
- return { protocols: [...protocols] };
4327
- }
4328
-
4329
4517
  // src/init/x402.ts
4330
- async function initX402(config, configError) {
4518
+ async function initX402(config, kvStore, configError) {
4331
4519
  if (configError) return { initError: configError };
4332
4520
  try {
4333
- const { createX402Server: createX402Server2 } = await Promise.resolve().then(() => (init_server(), server_exports));
4334
- const result = await createX402Server2(config);
4521
+ const { createX402Server: createX402Server2 } = await Promise.resolve().then(() => (init_x402_server(), x402_server_exports));
4522
+ const result = await createX402Server2(config, kvStore);
4335
4523
  await result.initPromise;
4336
4524
  return {
4337
4525
  server: result.server,
@@ -4342,7 +4530,7 @@ async function initX402(config, configError) {
4342
4530
  }
4343
4531
  }
4344
4532
 
4345
- // src/mppx-init.ts
4533
+ // src/init/mppx.ts
4346
4534
  function getMppxRequestContext(args) {
4347
4535
  const {
4348
4536
  Mppx,
@@ -4464,9 +4652,10 @@ function createRouter(config) {
4464
4652
  const kvStore = resolveKvStore(config.kvStore);
4465
4653
  const nonceStore = kvStore ? createKvNonceStore(kvStore) : new MemoryNonceStore();
4466
4654
  const entitlementStore = kvStore ? createKvEntitlementStore(kvStore) : new MemoryEntitlementStore();
4467
- const network = config.network ?? BASE_NETWORK;
4655
+ const network = config.network ?? BASE_MAINNET_NETWORK;
4468
4656
  const x402Accepts = getConfiguredX402Accepts(config);
4469
4657
  const configIssues = getRouterConfigIssues(config, {
4658
+ env: process.env,
4470
4659
  requireCdpKeys: process.env.NODE_ENV === "production"
4471
4660
  });
4472
4661
  const baseUrlIssue = configIssues.find((issue) => issue.code === "missing_base_url");
@@ -4513,7 +4702,7 @@ function createRouter(config) {
4513
4702
  mppSessionConfig: config.mpp?.session ? { depositMultiplier: config.mpp.session.depositMultiplier ?? 10 } : null
4514
4703
  };
4515
4704
  deps.initPromise = (async () => {
4516
- const x402Result = await initX402(config, x402ConfigError);
4705
+ const x402Result = await initX402(config, kvStore, x402ConfigError);
4517
4706
  deps.x402Server = x402Result.server ?? null;
4518
4707
  deps.x402FacilitatorsByNetwork = x402Result.facilitatorsByNetwork;
4519
4708
  if (x402Result.initError) deps.x402InitError = x402Result.initError;
@@ -4542,11 +4731,10 @@ function createRouter(config) {
4542
4731
  `[router] strictRoutes=true forbids key/path divergence for route '${definition.path}'. Remove custom \`key\` or make it equal to \`path\`.`
4543
4732
  );
4544
4733
  }
4545
- let builder = new RouteBuilder(key, registry, deps);
4734
+ let builder = new RouteBuilder(key, registry, deps, {
4735
+ protocols: config.protocols
4736
+ });
4546
4737
  builder = builder.path(normalizedPath);
4547
- if (config.protocols) {
4548
- builder._protocols = [...config.protocols];
4549
- }
4550
4738
  if (definition.method) {
4551
4739
  builder = builder.method(definition.method);
4552
4740
  }
@@ -4589,29 +4777,21 @@ function normalizePath(path) {
4589
4777
  normalized = normalized.replace(/^api\/+/, "");
4590
4778
  return normalized.replace(/\/+$/, "");
4591
4779
  }
4780
+ function createRouterFromEnv(options) {
4781
+ return createRouter(routerConfigFromEnv(options));
4782
+ }
4592
4783
  export {
4593
- BASE_NETWORK,
4784
+ BASE_MAINNET_NETWORK,
4785
+ BASE_USDC_ADDRESS,
4786
+ BASE_USDC_DECIMALS,
4787
+ DEFAULT_SOLANA_FACILITATOR_URL,
4594
4788
  HttpError,
4595
- MemoryEntitlementStore,
4596
- MemoryNonceStore,
4597
- RouteBuilder,
4598
- RouteRegistry,
4599
4789
  RouterConfigError,
4600
- SIWX_CHALLENGE_EXPIRY_MS,
4601
- SIWX_ERROR_MESSAGES,
4602
4790
  SOLANA_MAINNET_NETWORK,
4603
- TEMPO_USDC_CURRENCY,
4791
+ TEMPO_USDC_ADDRESS,
4792
+ TEMPO_USDC_DECIMALS,
4604
4793
  ZERO_EVM_ADDRESS,
4605
- consolePlugin,
4606
- createKvEntitlementStore,
4607
- createKvMppStore,
4608
- createKvNonceStore,
4609
4794
  createRouter,
4610
- formatRouterConfigIssues,
4611
- getRouterConfigIssues,
4612
- mppFromEnv,
4613
- paidOptionsForProtocols,
4614
- validateRouterConfig,
4615
- withPrefix,
4616
- x402AcceptsFromEnv
4795
+ createRouterFromEnv,
4796
+ routerConfigFromEnv
4617
4797
  };