@agentcash/router 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -35,14 +35,15 @@ var server_exports = {};
35
35
  __export(server_exports, {
36
36
  createX402Server: () => createX402Server
37
37
  });
38
- function createX402Server(config) {
39
- const { x402ResourceServer, HTTPFacilitatorClient } = require("@x402/core/server");
40
- const { registerExactEvmScheme } = require("@x402/evm/exact/server");
41
- const { bazaarResourceServerExtension } = require("@x402/extensions/bazaar");
42
- const { siwxResourceServerExtension } = require("@x402/extensions/sign-in-with-x");
43
- const { facilitator: defaultFacilitator } = require("@coinbase/x402");
44
- const facilitatorUrl = config.facilitatorUrl ?? defaultFacilitator;
45
- const client = new HTTPFacilitatorClient(facilitatorUrl);
38
+ async function createX402Server(config) {
39
+ const { x402ResourceServer, HTTPFacilitatorClient } = await import("@x402/core/server");
40
+ const { registerExactEvmScheme } = await import("@x402/evm/exact/server");
41
+ const { bazaarResourceServerExtension } = await import("@x402/extensions/bazaar");
42
+ const { siwxResourceServerExtension } = await import("@x402/extensions/sign-in-with-x");
43
+ const { facilitator: defaultFacilitator } = await import("@coinbase/x402");
44
+ const raw = config.facilitatorUrl ?? defaultFacilitator;
45
+ const facilitatorConfig = typeof raw === "string" ? { url: raw } : raw;
46
+ const client = new HTTPFacilitatorClient(facilitatorConfig);
46
47
  const server = new x402ResourceServer(client);
47
48
  registerExactEvmScheme(server);
48
49
  server.registerExtension(bazaarResourceServerExtension);
@@ -53,7 +54,7 @@ function createX402Server(config) {
53
54
  async function retryInit(server, maxAttempts = 3, backoff = [1e3, 2e3, 4e3]) {
54
55
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
55
56
  try {
56
- await server.init();
57
+ await server.initialize();
57
58
  return;
58
59
  } catch (err) {
59
60
  const is429 = err instanceof Error && (err.message.includes("429") || err.message.includes("rate limit"));
@@ -214,7 +215,7 @@ async function safeCallHandler(handler, ctx) {
214
215
  if (result instanceof Response) return result;
215
216
  return import_server.NextResponse.json(result);
216
217
  } catch (error) {
217
- const status = error instanceof HttpError ? error.status : 500;
218
+ const status = error instanceof HttpError ? error.status : typeof error.status === "number" ? error.status : 500;
218
219
  const message = error instanceof Error ? error.message : "Internal error";
219
220
  return import_server.NextResponse.json({ success: false, error: message }, { status });
220
221
  }
@@ -288,8 +289,8 @@ function resolveMaxPrice(pricing) {
288
289
  }
289
290
 
290
291
  // src/protocols/x402.ts
291
- function buildX402Challenge(server, routeEntry, request, price, payeeAddress, network, extensions) {
292
- const { encodePaymentRequiredHeader } = require("@x402/core/http");
292
+ async function buildX402Challenge(server, routeEntry, request, price, payeeAddress, network, extensions) {
293
+ const { encodePaymentRequiredHeader } = await import("@x402/core/http");
293
294
  const options = {
294
295
  scheme: "exact",
295
296
  network,
@@ -301,10 +302,10 @@ function buildX402Challenge(server, routeEntry, request, price, payeeAddress, ne
301
302
  method: routeEntry.method,
302
303
  description: routeEntry.description
303
304
  };
304
- const requirements = server.buildPaymentRequirementsFromOptions(options, {
305
+ const requirements = await server.buildPaymentRequirementsFromOptions([options], {
305
306
  request
306
307
  });
307
- const paymentRequired = server.createPaymentRequiredResponse(
308
+ const paymentRequired = await server.createPaymentRequiredResponse(
308
309
  requirements,
309
310
  resource,
310
311
  null,
@@ -314,7 +315,7 @@ function buildX402Challenge(server, routeEntry, request, price, payeeAddress, ne
314
315
  return { encoded, requirements };
315
316
  }
316
317
  async function verifyX402Payment(server, request, routeEntry, price, payeeAddress, network) {
317
- const { decodePaymentSignatureHeader } = require("@x402/core/http");
318
+ const { decodePaymentSignatureHeader } = await import("@x402/core/http");
318
319
  const paymentHeader = request.headers.get("PAYMENT-SIGNATURE") ?? request.headers.get("X-PAYMENT");
319
320
  if (!paymentHeader) return null;
320
321
  const payload = decodePaymentSignatureHeader(paymentHeader);
@@ -324,7 +325,7 @@ async function verifyX402Payment(server, request, routeEntry, price, payeeAddres
324
325
  price,
325
326
  payTo: payeeAddress
326
327
  };
327
- const requirements = server.buildPaymentRequirementsFromOptions(options, {
328
+ const requirements = await server.buildPaymentRequirementsFromOptions([options], {
328
329
  request
329
330
  });
330
331
  const matching = server.findMatchingRequirements(requirements, payload);
@@ -340,7 +341,7 @@ async function verifyX402Payment(server, request, routeEntry, price, payeeAddres
340
341
  };
341
342
  }
342
343
  async function settleX402Payment(server, payload, requirements) {
343
- const { encodePaymentResponseHeader } = require("@x402/core/http");
344
+ const { encodePaymentResponseHeader } = await import("@x402/core/http");
344
345
  const result = await server.settlePayment(payload, requirements);
345
346
  const encoded = encodePaymentResponseHeader(result);
346
347
  return { encoded, result };
@@ -352,28 +353,28 @@ var Challenge;
352
353
  var Credential;
353
354
  var Receipt;
354
355
  var tempo;
355
- function ensureMpay() {
356
+ async function ensureMpay() {
356
357
  if (mpayLoaded) return;
357
358
  try {
358
- const mpay = require("mpay");
359
+ const mpay = await import("mpay");
359
360
  Challenge = mpay.Challenge;
360
361
  Credential = mpay.Credential;
361
362
  Receipt = mpay.Receipt;
362
- const mpayServer = require("mpay/server");
363
+ const mpayServer = await import("mpay/server");
363
364
  tempo = mpayServer.tempo;
364
365
  mpayLoaded = true;
365
366
  } catch {
366
367
  throw new Error("mpay package is required for MPP protocol support. Install it: pnpm add mpay");
367
368
  }
368
369
  }
369
- function buildMPPChallenge(routeEntry, request, mppConfig, price) {
370
- ensureMpay();
371
- const intent = {
370
+ async function buildMPPChallenge(routeEntry, request, mppConfig, price) {
371
+ await ensureMpay();
372
+ const methodIntent = tempo.charge({
372
373
  amount: price,
373
374
  currency: mppConfig.currency,
374
375
  recipient: mppConfig.recipient ?? ""
375
- };
376
- const challenge = Challenge.fromIntent(intent, {
376
+ });
377
+ const challenge = Challenge.fromIntent(methodIntent, {
377
378
  secretKey: mppConfig.secretKey,
378
379
  realm: new URL(request.url).origin,
379
380
  request
@@ -381,10 +382,10 @@ function buildMPPChallenge(routeEntry, request, mppConfig, price) {
381
382
  return Challenge.serialize(challenge);
382
383
  }
383
384
  async function verifyMPPCredential(request, _routeEntry, mppConfig, price) {
384
- ensureMpay();
385
+ await ensureMpay();
385
386
  const credential = Credential.fromRequest(request);
386
387
  if (!credential) return null;
387
- const isValid = Challenge.verify(credential, { secretKey: mppConfig.secretKey });
388
+ const isValid = Challenge.verify(credential.challenge, { secretKey: mppConfig.secretKey });
388
389
  if (!isValid) {
389
390
  return { valid: false, payer: null };
390
391
  }
@@ -402,24 +403,20 @@ async function verifyMPPCredential(request, _routeEntry, mppConfig, price) {
402
403
  payer: verifyResult.payer
403
404
  };
404
405
  }
405
- function buildMPPReceipt(reference) {
406
- ensureMpay();
406
+ async function buildMPPReceipt(reference) {
407
+ await ensureMpay();
407
408
  const receipt = Receipt.from({
408
409
  method: "tempo",
409
410
  status: "success",
410
411
  reference,
411
- timestamp: Date.now()
412
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
412
413
  });
413
414
  return Receipt.serialize(receipt);
414
415
  }
415
416
 
416
417
  // src/auth/siwx.ts
417
418
  async function verifySIWX(request, _routeEntry, nonceStore) {
418
- const {
419
- parseSIWxHeader,
420
- validateSIWxMessage,
421
- verifySIWxSignature
422
- } = require("@x402/extensions/sign-in-with-x");
419
+ const { parseSIWxHeader, validateSIWxMessage, verifySIWxSignature } = await import("@x402/extensions/sign-in-with-x");
423
420
  const header = request.headers.get("SIGN-IN-WITH-X");
424
421
  if (!header) return { valid: false, wallet: null };
425
422
  const payload = parseSIWxHeader(header);
@@ -427,17 +424,17 @@ async function verifySIWX(request, _routeEntry, nonceStore) {
427
424
  const validation = await validateSIWxMessage(payload, uri, {
428
425
  checkNonce: (nonce) => nonceStore.check(nonce)
429
426
  });
430
- if (!validation.isValid) {
427
+ if (!validation.valid) {
431
428
  return { valid: false, wallet: null };
432
429
  }
433
430
  const verified = await verifySIWxSignature(payload);
434
- if (!verified?.isValid) {
431
+ if (!verified?.valid) {
435
432
  return { valid: false, wallet: null };
436
433
  }
437
434
  return { valid: true, wallet: verified.address };
438
435
  }
439
- function buildSIWXExtension() {
440
- const { declareSIWxExtension } = require("@x402/extensions/sign-in-with-x");
436
+ async function buildSIWXExtension() {
437
+ const { declareSIWxExtension } = await import("@x402/extensions/sign-in-with-x");
441
438
  return declareSIWxExtension();
442
439
  }
443
440
 
@@ -537,11 +534,56 @@ function createRequestHandler(routeEntry, handler, deps) {
537
534
  const protocol = detectProtocol(request);
538
535
  if (routeEntry.authMode === "siwx") {
539
536
  if (!request.headers.get("SIGN-IN-WITH-X")) {
540
- const response = new import_server2.NextResponse(null, { status: 402 });
537
+ const url = new URL(request.url);
538
+ const nonce = crypto.randomUUID();
539
+ const siwxInfo = {
540
+ domain: url.hostname,
541
+ uri: request.url,
542
+ version: "1",
543
+ chainId: deps.network,
544
+ type: "eip191",
545
+ nonce,
546
+ issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
547
+ expirationTime: new Date(Date.now() + 3e5).toISOString(),
548
+ statement: "Sign in to verify your wallet identity"
549
+ };
550
+ let siwxSchema;
541
551
  try {
542
- if (buildSIWXExtension()) response.headers.set("X-SIWX-REQUIRED", "true");
552
+ siwxSchema = await buildSIWXExtension();
543
553
  } catch {
544
554
  }
555
+ const paymentRequired = {
556
+ x402Version: 2,
557
+ error: "SIWX authentication required",
558
+ resource: {
559
+ url: request.url,
560
+ description: routeEntry.description ?? "SIWX-protected endpoint",
561
+ mimeType: "application/json"
562
+ },
563
+ accepts: [],
564
+ extensions: {
565
+ "sign-in-with-x": {
566
+ info: siwxInfo,
567
+ ...siwxSchema ? { schema: siwxSchema } : {}
568
+ }
569
+ }
570
+ };
571
+ let encoded;
572
+ try {
573
+ const { encodePaymentRequiredHeader } = await import("@x402/core/http");
574
+ encoded = encodePaymentRequiredHeader(paymentRequired);
575
+ } catch (err) {
576
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
577
+ level: "warn",
578
+ message: `SIWX challenge header encoding failed: ${err instanceof Error ? err.message : String(err)}`,
579
+ route: routeEntry.key
580
+ });
581
+ }
582
+ const response = new import_server2.NextResponse(JSON.stringify(paymentRequired), {
583
+ status: 402,
584
+ headers: { "Content-Type": "application/json" }
585
+ });
586
+ if (encoded) response.headers.set("PAYMENT-REQUIRED", encoded);
545
587
  firePluginResponse(deps, pluginCtx, meta, response);
546
588
  return response;
547
589
  }
@@ -558,7 +600,7 @@ function createRequestHandler(routeEntry, handler, deps) {
558
600
  return handleAuth(siwx.wallet, void 0);
559
601
  }
560
602
  if (!protocol || protocol === "siwx") {
561
- return build402(request, routeEntry, deps, meta, pluginCtx);
603
+ return await build402(request, routeEntry, deps, meta, pluginCtx);
562
604
  }
563
605
  const body = await parseBody(request, routeEntry);
564
606
  if (!body.ok) {
@@ -589,7 +631,7 @@ function createRequestHandler(routeEntry, handler, deps) {
589
631
  deps.payeeAddress,
590
632
  deps.network
591
633
  );
592
- if (!verify?.valid) return build402(request, routeEntry, deps, meta, pluginCtx);
634
+ if (!verify?.valid) return await build402(request, routeEntry, deps, meta, pluginCtx);
593
635
  pluginCtx.setVerifiedWallet(verify.payer);
594
636
  firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
595
637
  protocol: "x402",
@@ -631,9 +673,9 @@ function createRequestHandler(routeEntry, handler, deps) {
631
673
  return response;
632
674
  }
633
675
  if (protocol === "mpp") {
634
- if (!deps.mppConfig) return build402(request, routeEntry, deps, meta, pluginCtx);
676
+ if (!deps.mppConfig) return await build402(request, routeEntry, deps, meta, pluginCtx);
635
677
  const verify = await verifyMPPCredential(request, routeEntry, deps.mppConfig, price);
636
- if (!verify?.valid) return build402(request, routeEntry, deps, meta, pluginCtx);
678
+ if (!verify?.valid) return await build402(request, routeEntry, deps, meta, pluginCtx);
637
679
  const wallet = verify.payer;
638
680
  pluginCtx.setVerifiedWallet(wallet);
639
681
  firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
@@ -652,14 +694,14 @@ function createRequestHandler(routeEntry, handler, deps) {
652
694
  );
653
695
  if (response.status < 400) {
654
696
  try {
655
- response.headers.set("Payment-Receipt", buildMPPReceipt(crypto.randomUUID()));
697
+ response.headers.set("Payment-Receipt", await buildMPPReceipt(crypto.randomUUID()));
656
698
  } catch {
657
699
  }
658
700
  }
659
701
  finalize(response, rawResult, meta, pluginCtx);
660
702
  return response;
661
703
  }
662
- return build402(request, routeEntry, deps, meta, pluginCtx);
704
+ return await build402(request, routeEntry, deps, meta, pluginCtx);
663
705
  };
664
706
  }
665
707
  async function parseBody(request, routeEntry) {
@@ -696,7 +738,7 @@ function parseQuery(request, routeEntry) {
696
738
  const result = routeEntry.querySchema.safeParse(params);
697
739
  return result.success ? result.data : params;
698
740
  }
699
- function build402(request, routeEntry, deps, meta, pluginCtx) {
741
+ async function build402(request, routeEntry, deps, meta, pluginCtx) {
700
742
  const response = new import_server2.NextResponse(null, { status: 402 });
701
743
  let challengePrice;
702
744
  if (routeEntry.maxPrice) {
@@ -712,8 +754,8 @@ function build402(request, routeEntry, deps, meta, pluginCtx) {
712
754
  }
713
755
  let extensions;
714
756
  try {
715
- const { z } = require("zod");
716
- const { declareDiscoveryExtension } = require("@x402/extensions/bazaar");
757
+ const { z } = await import("zod");
758
+ const { declareDiscoveryExtension } = await import("@x402/extensions/bazaar");
717
759
  const inputSchema = routeEntry.bodySchema ? z.toJSONSchema(routeEntry.bodySchema, { target: "draft-2020-12" }) : routeEntry.querySchema ? z.toJSONSchema(routeEntry.querySchema, { target: "draft-2020-12" }) : void 0;
718
760
  const outputSchema = routeEntry.outputSchema ? z.toJSONSchema(routeEntry.outputSchema, { target: "draft-2020-12" }) : void 0;
719
761
  if (inputSchema) {
@@ -728,7 +770,7 @@ function build402(request, routeEntry, deps, meta, pluginCtx) {
728
770
  }
729
771
  if (routeEntry.protocols.includes("x402") && deps.x402Server) {
730
772
  try {
731
- const { encoded } = buildX402Challenge(
773
+ const { encoded } = await buildX402Challenge(
732
774
  deps.x402Server,
733
775
  routeEntry,
734
776
  request,
@@ -738,16 +780,26 @@ function build402(request, routeEntry, deps, meta, pluginCtx) {
738
780
  extensions
739
781
  );
740
782
  response.headers.set("PAYMENT-REQUIRED", encoded);
741
- } catch {
783
+ } catch (err) {
784
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
785
+ level: "critical",
786
+ message: `x402 challenge build failed: ${err instanceof Error ? err.message : String(err)}`,
787
+ route: routeEntry.key
788
+ });
742
789
  }
743
790
  }
744
791
  if (routeEntry.protocols.includes("mpp") && deps.mppConfig) {
745
792
  try {
746
793
  response.headers.set(
747
794
  "WWW-Authenticate",
748
- buildMPPChallenge(routeEntry, request, deps.mppConfig, challengePrice)
795
+ await buildMPPChallenge(routeEntry, request, deps.mppConfig, challengePrice)
749
796
  );
750
- } catch {
797
+ } catch (err) {
798
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
799
+ level: "critical",
800
+ message: `MPP challenge build failed: ${err instanceof Error ? err.message : String(err)}`,
801
+ route: routeEntry.key
802
+ });
751
803
  }
752
804
  }
753
805
  firePluginResponse(deps, pluginCtx, meta, response);
@@ -932,6 +984,11 @@ var RouteBuilder = class {
932
984
  next._path = p;
933
985
  return next;
934
986
  }
987
+ method(m) {
988
+ const next = this.fork();
989
+ next._method = m;
990
+ return next;
991
+ }
935
992
  handler(fn) {
936
993
  const entry = {
937
994
  key: this._key,
@@ -980,12 +1037,12 @@ function createWellKnownHandler(registry, baseUrl, pricesKeys, options = {}) {
980
1037
  registry.validate(pricesKeys);
981
1038
  validated = true;
982
1039
  }
983
- const x402Resources = [];
984
- const mppResources = [];
1040
+ const x402Set = /* @__PURE__ */ new Set();
1041
+ const mppSet = /* @__PURE__ */ new Set();
985
1042
  for (const [key, entry] of registry.entries()) {
986
1043
  const url = `${baseUrl}/api/${entry.path ?? key}`;
987
- if (entry.protocols.includes("x402")) x402Resources.push(url);
988
- if (entry.protocols.includes("mpp")) mppResources.push(url);
1044
+ if (entry.authMode !== "unprotected") x402Set.add(url);
1045
+ if (entry.protocols.includes("mpp")) mppSet.add(url);
989
1046
  }
990
1047
  let instructions;
991
1048
  if (typeof options.instructions === "function") {
@@ -995,18 +1052,28 @@ function createWellKnownHandler(registry, baseUrl, pricesKeys, options = {}) {
995
1052
  }
996
1053
  const body = {
997
1054
  version: 1,
998
- resources: x402Resources
1055
+ resources: Array.from(x402Set)
999
1056
  };
1057
+ const mppResources = Array.from(mppSet);
1000
1058
  if (mppResources.length > 0) {
1001
1059
  body.mppResources = mppResources;
1002
1060
  }
1061
+ if (options.description) {
1062
+ body.description = options.description;
1063
+ }
1003
1064
  if (options.ownershipProofs) {
1004
1065
  body.ownershipProofs = options.ownershipProofs;
1005
1066
  }
1006
1067
  if (instructions) {
1007
1068
  body.instructions = instructions;
1008
1069
  }
1009
- return import_server3.NextResponse.json(body);
1070
+ return import_server3.NextResponse.json(body, {
1071
+ headers: {
1072
+ "Access-Control-Allow-Origin": "*",
1073
+ "Access-Control-Allow-Methods": "GET",
1074
+ "Access-Control-Allow-Headers": "Content-Type"
1075
+ }
1076
+ });
1010
1077
  };
1011
1078
  }
1012
1079
 
@@ -1029,9 +1096,7 @@ function createOpenAPIHandler(registry, baseUrl, pricesKeys, options) {
1029
1096
  const method = entry.method.toLowerCase();
1030
1097
  const tag = deriveTag(key);
1031
1098
  tagSet.add(tag);
1032
- paths[apiPath] = {
1033
- [method]: buildOperation(key, entry, tag)
1034
- };
1099
+ paths[apiPath] = { ...paths[apiPath], [method]: buildOperation(key, entry, tag) };
1035
1100
  }
1036
1101
  cached = createDocument({
1037
1102
  openapi: "3.1.0",
@@ -1109,7 +1174,11 @@ function createRouter(config) {
1109
1174
  const baseUrl = typeof globalThis.process !== "undefined" ? process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000" : "http://localhost:3000";
1110
1175
  if (config.plugin?.init) {
1111
1176
  try {
1112
- config.plugin.init({ origin: baseUrl });
1177
+ const result = config.plugin.init({ origin: baseUrl });
1178
+ if (result && typeof result.catch === "function") {
1179
+ result.catch(() => {
1180
+ });
1181
+ }
1113
1182
  } catch {
1114
1183
  }
1115
1184
  }
@@ -1122,16 +1191,17 @@ function createRouter(config) {
1122
1191
  network,
1123
1192
  mppConfig: config.mpp
1124
1193
  };
1125
- try {
1126
- const { createX402Server: createX402Server2 } = (init_server(), __toCommonJS(server_exports));
1127
- const result = createX402Server2(config);
1128
- deps.x402Server = result.server;
1129
- deps.initPromise = result.initPromise.catch((err) => {
1194
+ deps.initPromise = (async () => {
1195
+ try {
1196
+ const { createX402Server: createX402Server2 } = await Promise.resolve().then(() => (init_server(), server_exports));
1197
+ const result = await createX402Server2(config);
1198
+ deps.x402Server = result.server;
1199
+ await result.initPromise;
1200
+ } catch (err) {
1130
1201
  deps.x402Server = null;
1131
1202
  deps.x402InitError = err instanceof Error ? err.message : String(err);
1132
- });
1133
- } catch {
1134
- }
1203
+ }
1204
+ })();
1135
1205
  const pricesKeys = config.prices ? Object.keys(config.prices) : void 0;
1136
1206
  return {
1137
1207
  route(key) {
package/dist/index.d.cts CHANGED
@@ -82,6 +82,28 @@ interface AlertEvent {
82
82
  meta?: Record<string, unknown>;
83
83
  }
84
84
  type AlertFn = (level: AlertLevel, message: string, meta?: Record<string, unknown>) => void;
85
+ interface X402Server {
86
+ initialize(): Promise<void>;
87
+ buildPaymentRequirementsFromOptions(options: Array<{
88
+ scheme: string;
89
+ network: string;
90
+ price: string;
91
+ payTo: string;
92
+ }>, context: {
93
+ request: Request;
94
+ }): Promise<unknown[]>;
95
+ createPaymentRequiredResponse(requirements: unknown[], resource: {
96
+ url: string;
97
+ method: string;
98
+ description?: string;
99
+ }, error: string | null, extensions?: Record<string, unknown>): Promise<unknown>;
100
+ findMatchingRequirements(requirements: unknown[], payload: unknown): unknown;
101
+ verifyPayment(payload: unknown, requirements: unknown): Promise<{
102
+ isValid: boolean;
103
+ payer?: string;
104
+ }>;
105
+ settlePayment(payload: unknown, requirements: unknown): Promise<unknown>;
106
+ }
85
107
  type ProtocolType = 'x402' | 'mpp';
86
108
  type AuthMode = 'paid' | 'siwx' | 'apiKey' | 'unprotected';
87
109
  interface TierConfig {
@@ -142,7 +164,7 @@ interface RouteEntry {
142
164
  outputSchema?: ZodType;
143
165
  description?: string;
144
166
  path?: string;
145
- method: 'GET' | 'POST';
167
+ method: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
146
168
  maxPrice?: string;
147
169
  apiKeyResolver?: (key: string) => unknown | Promise<unknown>;
148
170
  providerName?: string;
@@ -175,6 +197,7 @@ declare class RouteRegistry {
175
197
  }
176
198
 
177
199
  interface WellKnownOptions {
200
+ description?: string;
178
201
  instructions?: string | (() => string | Promise<string>);
179
202
  ownershipProofs?: string[];
180
203
  }
@@ -191,7 +214,7 @@ interface OpenAPIOptions {
191
214
  }
192
215
 
193
216
  interface OrchestrateDeps {
194
- x402Server: Record<string, Function> | null;
217
+ x402Server: X402Server | null;
195
218
  initPromise: Promise<void>;
196
219
  x402InitError?: string;
197
220
  plugin?: RouterPlugin;
@@ -220,7 +243,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, HasAuth extend
220
243
  /** @internal */ _outputSchema: ZodType | undefined;
221
244
  /** @internal */ _description: string | undefined;
222
245
  /** @internal */ _path: string | undefined;
223
- /** @internal */ _method: 'GET' | 'POST';
246
+ /** @internal */ _method: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
224
247
  /** @internal */ _apiKeyResolver: ((key: string) => unknown | Promise<unknown>) | undefined;
225
248
  /** @internal */ _providerName: string | undefined;
226
249
  /** @internal */ _providerConfig: ProviderConfig | undefined;
@@ -247,6 +270,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, HasAuth extend
247
270
  output(schema: ZodType): this;
248
271
  description(text: string): this;
249
272
  path(p: string): this;
273
+ method(m: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH'): this;
250
274
  handler(this: RouteBuilder<TBody, TQuery, True, true, false>, fn: never): never;
251
275
  handler(this: RouteBuilder<TBody, TQuery, false, boolean, boolean>, fn: never): never;
252
276
  handler(this: RouteBuilder<TBody, TQuery, True, False, HasBody>, fn: (ctx: HandlerContext<TBody, TQuery>) => Promise<unknown>): (request: NextRequest) => Promise<Response>;
@@ -270,4 +294,4 @@ interface ServiceRouter {
270
294
  }
271
295
  declare function createRouter(config: RouterConfig): ServiceRouter;
272
296
 
273
- export { type AlertEvent, type AlertFn, type AlertLevel, type AuthMode, type ErrorEvent, type HandlerContext, HttpError, MemoryNonceStore, type MonitorEntry, type NonceStore, type OveragePolicy, type PaidOptions, type PaymentEvent, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, type RouterPlugin, type ServiceRouter, type SettlementEvent, type TierConfig, consolePlugin, createRouter };
297
+ export { type AlertEvent, type AlertFn, type AlertLevel, type AuthMode, type ErrorEvent, type HandlerContext, HttpError, MemoryNonceStore, type MonitorEntry, type NonceStore, type OveragePolicy, type PaidOptions, type PaymentEvent, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, type RouterPlugin, type ServiceRouter, type SettlementEvent, type TierConfig, type X402Server, consolePlugin, createRouter };
package/dist/index.d.ts CHANGED
@@ -82,6 +82,28 @@ interface AlertEvent {
82
82
  meta?: Record<string, unknown>;
83
83
  }
84
84
  type AlertFn = (level: AlertLevel, message: string, meta?: Record<string, unknown>) => void;
85
+ interface X402Server {
86
+ initialize(): Promise<void>;
87
+ buildPaymentRequirementsFromOptions(options: Array<{
88
+ scheme: string;
89
+ network: string;
90
+ price: string;
91
+ payTo: string;
92
+ }>, context: {
93
+ request: Request;
94
+ }): Promise<unknown[]>;
95
+ createPaymentRequiredResponse(requirements: unknown[], resource: {
96
+ url: string;
97
+ method: string;
98
+ description?: string;
99
+ }, error: string | null, extensions?: Record<string, unknown>): Promise<unknown>;
100
+ findMatchingRequirements(requirements: unknown[], payload: unknown): unknown;
101
+ verifyPayment(payload: unknown, requirements: unknown): Promise<{
102
+ isValid: boolean;
103
+ payer?: string;
104
+ }>;
105
+ settlePayment(payload: unknown, requirements: unknown): Promise<unknown>;
106
+ }
85
107
  type ProtocolType = 'x402' | 'mpp';
86
108
  type AuthMode = 'paid' | 'siwx' | 'apiKey' | 'unprotected';
87
109
  interface TierConfig {
@@ -142,7 +164,7 @@ interface RouteEntry {
142
164
  outputSchema?: ZodType;
143
165
  description?: string;
144
166
  path?: string;
145
- method: 'GET' | 'POST';
167
+ method: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
146
168
  maxPrice?: string;
147
169
  apiKeyResolver?: (key: string) => unknown | Promise<unknown>;
148
170
  providerName?: string;
@@ -175,6 +197,7 @@ declare class RouteRegistry {
175
197
  }
176
198
 
177
199
  interface WellKnownOptions {
200
+ description?: string;
178
201
  instructions?: string | (() => string | Promise<string>);
179
202
  ownershipProofs?: string[];
180
203
  }
@@ -191,7 +214,7 @@ interface OpenAPIOptions {
191
214
  }
192
215
 
193
216
  interface OrchestrateDeps {
194
- x402Server: Record<string, Function> | null;
217
+ x402Server: X402Server | null;
195
218
  initPromise: Promise<void>;
196
219
  x402InitError?: string;
197
220
  plugin?: RouterPlugin;
@@ -220,7 +243,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, HasAuth extend
220
243
  /** @internal */ _outputSchema: ZodType | undefined;
221
244
  /** @internal */ _description: string | undefined;
222
245
  /** @internal */ _path: string | undefined;
223
- /** @internal */ _method: 'GET' | 'POST';
246
+ /** @internal */ _method: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
224
247
  /** @internal */ _apiKeyResolver: ((key: string) => unknown | Promise<unknown>) | undefined;
225
248
  /** @internal */ _providerName: string | undefined;
226
249
  /** @internal */ _providerConfig: ProviderConfig | undefined;
@@ -247,6 +270,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, HasAuth extend
247
270
  output(schema: ZodType): this;
248
271
  description(text: string): this;
249
272
  path(p: string): this;
273
+ method(m: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH'): this;
250
274
  handler(this: RouteBuilder<TBody, TQuery, True, true, false>, fn: never): never;
251
275
  handler(this: RouteBuilder<TBody, TQuery, false, boolean, boolean>, fn: never): never;
252
276
  handler(this: RouteBuilder<TBody, TQuery, True, False, HasBody>, fn: (ctx: HandlerContext<TBody, TQuery>) => Promise<unknown>): (request: NextRequest) => Promise<Response>;
@@ -270,4 +294,4 @@ interface ServiceRouter {
270
294
  }
271
295
  declare function createRouter(config: RouterConfig): ServiceRouter;
272
296
 
273
- export { type AlertEvent, type AlertFn, type AlertLevel, type AuthMode, type ErrorEvent, type HandlerContext, HttpError, MemoryNonceStore, type MonitorEntry, type NonceStore, type OveragePolicy, type PaidOptions, type PaymentEvent, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, type RouterPlugin, type ServiceRouter, type SettlementEvent, type TierConfig, consolePlugin, createRouter };
297
+ export { type AlertEvent, type AlertFn, type AlertLevel, type AuthMode, type ErrorEvent, type HandlerContext, HttpError, MemoryNonceStore, type MonitorEntry, type NonceStore, type OveragePolicy, type PaidOptions, type PaymentEvent, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, type RouterPlugin, type ServiceRouter, type SettlementEvent, type TierConfig, type X402Server, consolePlugin, createRouter };
package/dist/index.js CHANGED
@@ -1,13 +1,5 @@
1
1
  var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
- }) : x)(function(x) {
8
- if (typeof require !== "undefined") return require.apply(this, arguments);
9
- throw Error('Dynamic require of "' + x + '" is not supported');
10
- });
11
3
  var __esm = (fn, res) => function __init() {
12
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
5
  };
@@ -15,29 +7,21 @@ var __export = (target, all) => {
15
7
  for (var name in all)
16
8
  __defProp(target, name, { get: all[name], enumerable: true });
17
9
  };
18
- var __copyProps = (to, from, except, desc) => {
19
- if (from && typeof from === "object" || typeof from === "function") {
20
- for (let key of __getOwnPropNames(from))
21
- if (!__hasOwnProp.call(to, key) && key !== except)
22
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
- }
24
- return to;
25
- };
26
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
10
 
28
11
  // src/server.ts
29
12
  var server_exports = {};
30
13
  __export(server_exports, {
31
14
  createX402Server: () => createX402Server
32
15
  });
33
- function createX402Server(config) {
34
- const { x402ResourceServer, HTTPFacilitatorClient } = __require("@x402/core/server");
35
- const { registerExactEvmScheme } = __require("@x402/evm/exact/server");
36
- const { bazaarResourceServerExtension } = __require("@x402/extensions/bazaar");
37
- const { siwxResourceServerExtension } = __require("@x402/extensions/sign-in-with-x");
38
- const { facilitator: defaultFacilitator } = __require("@coinbase/x402");
39
- const facilitatorUrl = config.facilitatorUrl ?? defaultFacilitator;
40
- const client = new HTTPFacilitatorClient(facilitatorUrl);
16
+ async function createX402Server(config) {
17
+ const { x402ResourceServer, HTTPFacilitatorClient } = await import("@x402/core/server");
18
+ const { registerExactEvmScheme } = await import("@x402/evm/exact/server");
19
+ const { bazaarResourceServerExtension } = await import("@x402/extensions/bazaar");
20
+ const { siwxResourceServerExtension } = await import("@x402/extensions/sign-in-with-x");
21
+ const { facilitator: defaultFacilitator } = await import("@coinbase/x402");
22
+ const raw = config.facilitatorUrl ?? defaultFacilitator;
23
+ const facilitatorConfig = typeof raw === "string" ? { url: raw } : raw;
24
+ const client = new HTTPFacilitatorClient(facilitatorConfig);
41
25
  const server = new x402ResourceServer(client);
42
26
  registerExactEvmScheme(server);
43
27
  server.registerExtension(bazaarResourceServerExtension);
@@ -48,7 +32,7 @@ function createX402Server(config) {
48
32
  async function retryInit(server, maxAttempts = 3, backoff = [1e3, 2e3, 4e3]) {
49
33
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
50
34
  try {
51
- await server.init();
35
+ await server.initialize();
52
36
  return;
53
37
  } catch (err) {
54
38
  const is429 = err instanceof Error && (err.message.includes("429") || err.message.includes("rate limit"));
@@ -197,7 +181,7 @@ async function safeCallHandler(handler, ctx) {
197
181
  if (result instanceof Response) return result;
198
182
  return NextResponse.json(result);
199
183
  } catch (error) {
200
- const status = error instanceof HttpError ? error.status : 500;
184
+ const status = error instanceof HttpError ? error.status : typeof error.status === "number" ? error.status : 500;
201
185
  const message = error instanceof Error ? error.message : "Internal error";
202
186
  return NextResponse.json({ success: false, error: message }, { status });
203
187
  }
@@ -271,8 +255,8 @@ function resolveMaxPrice(pricing) {
271
255
  }
272
256
 
273
257
  // src/protocols/x402.ts
274
- function buildX402Challenge(server, routeEntry, request, price, payeeAddress, network, extensions) {
275
- const { encodePaymentRequiredHeader } = __require("@x402/core/http");
258
+ async function buildX402Challenge(server, routeEntry, request, price, payeeAddress, network, extensions) {
259
+ const { encodePaymentRequiredHeader } = await import("@x402/core/http");
276
260
  const options = {
277
261
  scheme: "exact",
278
262
  network,
@@ -284,10 +268,10 @@ function buildX402Challenge(server, routeEntry, request, price, payeeAddress, ne
284
268
  method: routeEntry.method,
285
269
  description: routeEntry.description
286
270
  };
287
- const requirements = server.buildPaymentRequirementsFromOptions(options, {
271
+ const requirements = await server.buildPaymentRequirementsFromOptions([options], {
288
272
  request
289
273
  });
290
- const paymentRequired = server.createPaymentRequiredResponse(
274
+ const paymentRequired = await server.createPaymentRequiredResponse(
291
275
  requirements,
292
276
  resource,
293
277
  null,
@@ -297,7 +281,7 @@ function buildX402Challenge(server, routeEntry, request, price, payeeAddress, ne
297
281
  return { encoded, requirements };
298
282
  }
299
283
  async function verifyX402Payment(server, request, routeEntry, price, payeeAddress, network) {
300
- const { decodePaymentSignatureHeader } = __require("@x402/core/http");
284
+ const { decodePaymentSignatureHeader } = await import("@x402/core/http");
301
285
  const paymentHeader = request.headers.get("PAYMENT-SIGNATURE") ?? request.headers.get("X-PAYMENT");
302
286
  if (!paymentHeader) return null;
303
287
  const payload = decodePaymentSignatureHeader(paymentHeader);
@@ -307,7 +291,7 @@ async function verifyX402Payment(server, request, routeEntry, price, payeeAddres
307
291
  price,
308
292
  payTo: payeeAddress
309
293
  };
310
- const requirements = server.buildPaymentRequirementsFromOptions(options, {
294
+ const requirements = await server.buildPaymentRequirementsFromOptions([options], {
311
295
  request
312
296
  });
313
297
  const matching = server.findMatchingRequirements(requirements, payload);
@@ -323,7 +307,7 @@ async function verifyX402Payment(server, request, routeEntry, price, payeeAddres
323
307
  };
324
308
  }
325
309
  async function settleX402Payment(server, payload, requirements) {
326
- const { encodePaymentResponseHeader } = __require("@x402/core/http");
310
+ const { encodePaymentResponseHeader } = await import("@x402/core/http");
327
311
  const result = await server.settlePayment(payload, requirements);
328
312
  const encoded = encodePaymentResponseHeader(result);
329
313
  return { encoded, result };
@@ -335,28 +319,28 @@ var Challenge;
335
319
  var Credential;
336
320
  var Receipt;
337
321
  var tempo;
338
- function ensureMpay() {
322
+ async function ensureMpay() {
339
323
  if (mpayLoaded) return;
340
324
  try {
341
- const mpay = __require("mpay");
325
+ const mpay = await import("mpay");
342
326
  Challenge = mpay.Challenge;
343
327
  Credential = mpay.Credential;
344
328
  Receipt = mpay.Receipt;
345
- const mpayServer = __require("mpay/server");
329
+ const mpayServer = await import("mpay/server");
346
330
  tempo = mpayServer.tempo;
347
331
  mpayLoaded = true;
348
332
  } catch {
349
333
  throw new Error("mpay package is required for MPP protocol support. Install it: pnpm add mpay");
350
334
  }
351
335
  }
352
- function buildMPPChallenge(routeEntry, request, mppConfig, price) {
353
- ensureMpay();
354
- const intent = {
336
+ async function buildMPPChallenge(routeEntry, request, mppConfig, price) {
337
+ await ensureMpay();
338
+ const methodIntent = tempo.charge({
355
339
  amount: price,
356
340
  currency: mppConfig.currency,
357
341
  recipient: mppConfig.recipient ?? ""
358
- };
359
- const challenge = Challenge.fromIntent(intent, {
342
+ });
343
+ const challenge = Challenge.fromIntent(methodIntent, {
360
344
  secretKey: mppConfig.secretKey,
361
345
  realm: new URL(request.url).origin,
362
346
  request
@@ -364,10 +348,10 @@ function buildMPPChallenge(routeEntry, request, mppConfig, price) {
364
348
  return Challenge.serialize(challenge);
365
349
  }
366
350
  async function verifyMPPCredential(request, _routeEntry, mppConfig, price) {
367
- ensureMpay();
351
+ await ensureMpay();
368
352
  const credential = Credential.fromRequest(request);
369
353
  if (!credential) return null;
370
- const isValid = Challenge.verify(credential, { secretKey: mppConfig.secretKey });
354
+ const isValid = Challenge.verify(credential.challenge, { secretKey: mppConfig.secretKey });
371
355
  if (!isValid) {
372
356
  return { valid: false, payer: null };
373
357
  }
@@ -385,24 +369,20 @@ async function verifyMPPCredential(request, _routeEntry, mppConfig, price) {
385
369
  payer: verifyResult.payer
386
370
  };
387
371
  }
388
- function buildMPPReceipt(reference) {
389
- ensureMpay();
372
+ async function buildMPPReceipt(reference) {
373
+ await ensureMpay();
390
374
  const receipt = Receipt.from({
391
375
  method: "tempo",
392
376
  status: "success",
393
377
  reference,
394
- timestamp: Date.now()
378
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
395
379
  });
396
380
  return Receipt.serialize(receipt);
397
381
  }
398
382
 
399
383
  // src/auth/siwx.ts
400
384
  async function verifySIWX(request, _routeEntry, nonceStore) {
401
- const {
402
- parseSIWxHeader,
403
- validateSIWxMessage,
404
- verifySIWxSignature
405
- } = __require("@x402/extensions/sign-in-with-x");
385
+ const { parseSIWxHeader, validateSIWxMessage, verifySIWxSignature } = await import("@x402/extensions/sign-in-with-x");
406
386
  const header = request.headers.get("SIGN-IN-WITH-X");
407
387
  if (!header) return { valid: false, wallet: null };
408
388
  const payload = parseSIWxHeader(header);
@@ -410,17 +390,17 @@ async function verifySIWX(request, _routeEntry, nonceStore) {
410
390
  const validation = await validateSIWxMessage(payload, uri, {
411
391
  checkNonce: (nonce) => nonceStore.check(nonce)
412
392
  });
413
- if (!validation.isValid) {
393
+ if (!validation.valid) {
414
394
  return { valid: false, wallet: null };
415
395
  }
416
396
  const verified = await verifySIWxSignature(payload);
417
- if (!verified?.isValid) {
397
+ if (!verified?.valid) {
418
398
  return { valid: false, wallet: null };
419
399
  }
420
400
  return { valid: true, wallet: verified.address };
421
401
  }
422
- function buildSIWXExtension() {
423
- const { declareSIWxExtension } = __require("@x402/extensions/sign-in-with-x");
402
+ async function buildSIWXExtension() {
403
+ const { declareSIWxExtension } = await import("@x402/extensions/sign-in-with-x");
424
404
  return declareSIWxExtension();
425
405
  }
426
406
 
@@ -520,11 +500,56 @@ function createRequestHandler(routeEntry, handler, deps) {
520
500
  const protocol = detectProtocol(request);
521
501
  if (routeEntry.authMode === "siwx") {
522
502
  if (!request.headers.get("SIGN-IN-WITH-X")) {
523
- const response = new NextResponse2(null, { status: 402 });
503
+ const url = new URL(request.url);
504
+ const nonce = crypto.randomUUID();
505
+ const siwxInfo = {
506
+ domain: url.hostname,
507
+ uri: request.url,
508
+ version: "1",
509
+ chainId: deps.network,
510
+ type: "eip191",
511
+ nonce,
512
+ issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
513
+ expirationTime: new Date(Date.now() + 3e5).toISOString(),
514
+ statement: "Sign in to verify your wallet identity"
515
+ };
516
+ let siwxSchema;
524
517
  try {
525
- if (buildSIWXExtension()) response.headers.set("X-SIWX-REQUIRED", "true");
518
+ siwxSchema = await buildSIWXExtension();
526
519
  } catch {
527
520
  }
521
+ const paymentRequired = {
522
+ x402Version: 2,
523
+ error: "SIWX authentication required",
524
+ resource: {
525
+ url: request.url,
526
+ description: routeEntry.description ?? "SIWX-protected endpoint",
527
+ mimeType: "application/json"
528
+ },
529
+ accepts: [],
530
+ extensions: {
531
+ "sign-in-with-x": {
532
+ info: siwxInfo,
533
+ ...siwxSchema ? { schema: siwxSchema } : {}
534
+ }
535
+ }
536
+ };
537
+ let encoded;
538
+ try {
539
+ const { encodePaymentRequiredHeader } = await import("@x402/core/http");
540
+ encoded = encodePaymentRequiredHeader(paymentRequired);
541
+ } catch (err) {
542
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
543
+ level: "warn",
544
+ message: `SIWX challenge header encoding failed: ${err instanceof Error ? err.message : String(err)}`,
545
+ route: routeEntry.key
546
+ });
547
+ }
548
+ const response = new NextResponse2(JSON.stringify(paymentRequired), {
549
+ status: 402,
550
+ headers: { "Content-Type": "application/json" }
551
+ });
552
+ if (encoded) response.headers.set("PAYMENT-REQUIRED", encoded);
528
553
  firePluginResponse(deps, pluginCtx, meta, response);
529
554
  return response;
530
555
  }
@@ -541,7 +566,7 @@ function createRequestHandler(routeEntry, handler, deps) {
541
566
  return handleAuth(siwx.wallet, void 0);
542
567
  }
543
568
  if (!protocol || protocol === "siwx") {
544
- return build402(request, routeEntry, deps, meta, pluginCtx);
569
+ return await build402(request, routeEntry, deps, meta, pluginCtx);
545
570
  }
546
571
  const body = await parseBody(request, routeEntry);
547
572
  if (!body.ok) {
@@ -572,7 +597,7 @@ function createRequestHandler(routeEntry, handler, deps) {
572
597
  deps.payeeAddress,
573
598
  deps.network
574
599
  );
575
- if (!verify?.valid) return build402(request, routeEntry, deps, meta, pluginCtx);
600
+ if (!verify?.valid) return await build402(request, routeEntry, deps, meta, pluginCtx);
576
601
  pluginCtx.setVerifiedWallet(verify.payer);
577
602
  firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
578
603
  protocol: "x402",
@@ -614,9 +639,9 @@ function createRequestHandler(routeEntry, handler, deps) {
614
639
  return response;
615
640
  }
616
641
  if (protocol === "mpp") {
617
- if (!deps.mppConfig) return build402(request, routeEntry, deps, meta, pluginCtx);
642
+ if (!deps.mppConfig) return await build402(request, routeEntry, deps, meta, pluginCtx);
618
643
  const verify = await verifyMPPCredential(request, routeEntry, deps.mppConfig, price);
619
- if (!verify?.valid) return build402(request, routeEntry, deps, meta, pluginCtx);
644
+ if (!verify?.valid) return await build402(request, routeEntry, deps, meta, pluginCtx);
620
645
  const wallet = verify.payer;
621
646
  pluginCtx.setVerifiedWallet(wallet);
622
647
  firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
@@ -635,14 +660,14 @@ function createRequestHandler(routeEntry, handler, deps) {
635
660
  );
636
661
  if (response.status < 400) {
637
662
  try {
638
- response.headers.set("Payment-Receipt", buildMPPReceipt(crypto.randomUUID()));
663
+ response.headers.set("Payment-Receipt", await buildMPPReceipt(crypto.randomUUID()));
639
664
  } catch {
640
665
  }
641
666
  }
642
667
  finalize(response, rawResult, meta, pluginCtx);
643
668
  return response;
644
669
  }
645
- return build402(request, routeEntry, deps, meta, pluginCtx);
670
+ return await build402(request, routeEntry, deps, meta, pluginCtx);
646
671
  };
647
672
  }
648
673
  async function parseBody(request, routeEntry) {
@@ -679,7 +704,7 @@ function parseQuery(request, routeEntry) {
679
704
  const result = routeEntry.querySchema.safeParse(params);
680
705
  return result.success ? result.data : params;
681
706
  }
682
- function build402(request, routeEntry, deps, meta, pluginCtx) {
707
+ async function build402(request, routeEntry, deps, meta, pluginCtx) {
683
708
  const response = new NextResponse2(null, { status: 402 });
684
709
  let challengePrice;
685
710
  if (routeEntry.maxPrice) {
@@ -695,8 +720,8 @@ function build402(request, routeEntry, deps, meta, pluginCtx) {
695
720
  }
696
721
  let extensions;
697
722
  try {
698
- const { z } = __require("zod");
699
- const { declareDiscoveryExtension } = __require("@x402/extensions/bazaar");
723
+ const { z } = await import("zod");
724
+ const { declareDiscoveryExtension } = await import("@x402/extensions/bazaar");
700
725
  const inputSchema = routeEntry.bodySchema ? z.toJSONSchema(routeEntry.bodySchema, { target: "draft-2020-12" }) : routeEntry.querySchema ? z.toJSONSchema(routeEntry.querySchema, { target: "draft-2020-12" }) : void 0;
701
726
  const outputSchema = routeEntry.outputSchema ? z.toJSONSchema(routeEntry.outputSchema, { target: "draft-2020-12" }) : void 0;
702
727
  if (inputSchema) {
@@ -711,7 +736,7 @@ function build402(request, routeEntry, deps, meta, pluginCtx) {
711
736
  }
712
737
  if (routeEntry.protocols.includes("x402") && deps.x402Server) {
713
738
  try {
714
- const { encoded } = buildX402Challenge(
739
+ const { encoded } = await buildX402Challenge(
715
740
  deps.x402Server,
716
741
  routeEntry,
717
742
  request,
@@ -721,16 +746,26 @@ function build402(request, routeEntry, deps, meta, pluginCtx) {
721
746
  extensions
722
747
  );
723
748
  response.headers.set("PAYMENT-REQUIRED", encoded);
724
- } catch {
749
+ } catch (err) {
750
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
751
+ level: "critical",
752
+ message: `x402 challenge build failed: ${err instanceof Error ? err.message : String(err)}`,
753
+ route: routeEntry.key
754
+ });
725
755
  }
726
756
  }
727
757
  if (routeEntry.protocols.includes("mpp") && deps.mppConfig) {
728
758
  try {
729
759
  response.headers.set(
730
760
  "WWW-Authenticate",
731
- buildMPPChallenge(routeEntry, request, deps.mppConfig, challengePrice)
761
+ await buildMPPChallenge(routeEntry, request, deps.mppConfig, challengePrice)
732
762
  );
733
- } catch {
763
+ } catch (err) {
764
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
765
+ level: "critical",
766
+ message: `MPP challenge build failed: ${err instanceof Error ? err.message : String(err)}`,
767
+ route: routeEntry.key
768
+ });
734
769
  }
735
770
  }
736
771
  firePluginResponse(deps, pluginCtx, meta, response);
@@ -915,6 +950,11 @@ var RouteBuilder = class {
915
950
  next._path = p;
916
951
  return next;
917
952
  }
953
+ method(m) {
954
+ const next = this.fork();
955
+ next._method = m;
956
+ return next;
957
+ }
918
958
  handler(fn) {
919
959
  const entry = {
920
960
  key: this._key,
@@ -963,12 +1003,12 @@ function createWellKnownHandler(registry, baseUrl, pricesKeys, options = {}) {
963
1003
  registry.validate(pricesKeys);
964
1004
  validated = true;
965
1005
  }
966
- const x402Resources = [];
967
- const mppResources = [];
1006
+ const x402Set = /* @__PURE__ */ new Set();
1007
+ const mppSet = /* @__PURE__ */ new Set();
968
1008
  for (const [key, entry] of registry.entries()) {
969
1009
  const url = `${baseUrl}/api/${entry.path ?? key}`;
970
- if (entry.protocols.includes("x402")) x402Resources.push(url);
971
- if (entry.protocols.includes("mpp")) mppResources.push(url);
1010
+ if (entry.authMode !== "unprotected") x402Set.add(url);
1011
+ if (entry.protocols.includes("mpp")) mppSet.add(url);
972
1012
  }
973
1013
  let instructions;
974
1014
  if (typeof options.instructions === "function") {
@@ -978,18 +1018,28 @@ function createWellKnownHandler(registry, baseUrl, pricesKeys, options = {}) {
978
1018
  }
979
1019
  const body = {
980
1020
  version: 1,
981
- resources: x402Resources
1021
+ resources: Array.from(x402Set)
982
1022
  };
1023
+ const mppResources = Array.from(mppSet);
983
1024
  if (mppResources.length > 0) {
984
1025
  body.mppResources = mppResources;
985
1026
  }
1027
+ if (options.description) {
1028
+ body.description = options.description;
1029
+ }
986
1030
  if (options.ownershipProofs) {
987
1031
  body.ownershipProofs = options.ownershipProofs;
988
1032
  }
989
1033
  if (instructions) {
990
1034
  body.instructions = instructions;
991
1035
  }
992
- return NextResponse3.json(body);
1036
+ return NextResponse3.json(body, {
1037
+ headers: {
1038
+ "Access-Control-Allow-Origin": "*",
1039
+ "Access-Control-Allow-Methods": "GET",
1040
+ "Access-Control-Allow-Headers": "Content-Type"
1041
+ }
1042
+ });
993
1043
  };
994
1044
  }
995
1045
 
@@ -1012,9 +1062,7 @@ function createOpenAPIHandler(registry, baseUrl, pricesKeys, options) {
1012
1062
  const method = entry.method.toLowerCase();
1013
1063
  const tag = deriveTag(key);
1014
1064
  tagSet.add(tag);
1015
- paths[apiPath] = {
1016
- [method]: buildOperation(key, entry, tag)
1017
- };
1065
+ paths[apiPath] = { ...paths[apiPath], [method]: buildOperation(key, entry, tag) };
1018
1066
  }
1019
1067
  cached = createDocument({
1020
1068
  openapi: "3.1.0",
@@ -1092,7 +1140,11 @@ function createRouter(config) {
1092
1140
  const baseUrl = typeof globalThis.process !== "undefined" ? process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000" : "http://localhost:3000";
1093
1141
  if (config.plugin?.init) {
1094
1142
  try {
1095
- config.plugin.init({ origin: baseUrl });
1143
+ const result = config.plugin.init({ origin: baseUrl });
1144
+ if (result && typeof result.catch === "function") {
1145
+ result.catch(() => {
1146
+ });
1147
+ }
1096
1148
  } catch {
1097
1149
  }
1098
1150
  }
@@ -1105,16 +1157,17 @@ function createRouter(config) {
1105
1157
  network,
1106
1158
  mppConfig: config.mpp
1107
1159
  };
1108
- try {
1109
- const { createX402Server: createX402Server2 } = (init_server(), __toCommonJS(server_exports));
1110
- const result = createX402Server2(config);
1111
- deps.x402Server = result.server;
1112
- deps.initPromise = result.initPromise.catch((err) => {
1160
+ deps.initPromise = (async () => {
1161
+ try {
1162
+ const { createX402Server: createX402Server2 } = await Promise.resolve().then(() => (init_server(), server_exports));
1163
+ const result = await createX402Server2(config);
1164
+ deps.x402Server = result.server;
1165
+ await result.initPromise;
1166
+ } catch (err) {
1113
1167
  deps.x402Server = null;
1114
1168
  deps.x402InitError = err instanceof Error ? err.message : String(err);
1115
- });
1116
- } catch {
1117
- }
1169
+ }
1170
+ })();
1118
1171
  const pricesKeys = config.prices ? Object.keys(config.prices) : void 0;
1119
1172
  return {
1120
1173
  route(key) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/router",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Unified route builder for Next.js App Router APIs with x402, MPP, SIWX, and API key auth",
5
5
  "type": "module",
6
6
  "exports": {
@@ -21,6 +21,17 @@
21
21
  "files": [
22
22
  "dist"
23
23
  ],
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "typecheck": "tsc --noEmit",
27
+ "lint": "eslint src/",
28
+ "lint:fix": "eslint src/ --fix",
29
+ "format": "prettier --write 'src/**/*.ts' 'tests/**/*.ts' '*.json' '*.mjs'",
30
+ "format:check": "prettier --check 'src/**/*.ts' 'tests/**/*.ts' '*.json' '*.mjs'",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest",
33
+ "check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm build && pnpm test"
34
+ },
24
35
  "peerDependencies": {
25
36
  "@coinbase/x402": "^2.1.0",
26
37
  "@x402/core": "^2.3.0",
@@ -55,20 +66,10 @@
55
66
  "zod": "^4.0.0",
56
67
  "zod-openapi": "^5.0.0"
57
68
  },
69
+ "packageManager": "pnpm@10.28.0",
58
70
  "license": "MIT",
59
71
  "repository": {
60
72
  "type": "git",
61
- "url": "https://github.com/merit-systems/agentcash-router"
62
- },
63
- "scripts": {
64
- "build": "tsup",
65
- "typecheck": "tsc --noEmit",
66
- "lint": "eslint src/",
67
- "lint:fix": "eslint src/ --fix",
68
- "format": "prettier --write 'src/**/*.ts' 'tests/**/*.ts' '*.json' '*.mjs'",
69
- "format:check": "prettier --check 'src/**/*.ts' 'tests/**/*.ts' '*.json' '*.mjs'",
70
- "test": "vitest run",
71
- "test:watch": "vitest",
72
- "check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm build && pnpm test"
73
+ "url": "git+https://github.com/merit-systems/agentcash-router.git"
73
74
  }
74
- }
75
+ }