@agentcash/router 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,6 +18,36 @@ pnpm add next zod @x402/core @x402/evm @x402/extensions @coinbase/x402 zod-opena
18
18
  pnpm add mpay
19
19
  ```
20
20
 
21
+ ## Environment Setup
22
+
23
+ The router uses the default facilitator from `@coinbase/x402` for x402 payments, which requires CDP API keys:
24
+
25
+ ```bash
26
+ CDP_API_KEY_ID=your-key-id
27
+ CDP_API_KEY_SECRET=your-key-secret
28
+ ```
29
+
30
+ **For Next.js apps with env validation** (T3 stack, `@t3-oss/env-nextjs`): Add these to your env schema — Next.js doesn't expose undeclared env vars to `process.env`.
31
+
32
+ ```typescript
33
+ // src/env.js
34
+ import { createEnv } from "@t3-oss/env-nextjs";
35
+ import { z } from "zod";
36
+
37
+ export const env = createEnv({
38
+ server: {
39
+ CDP_API_KEY_ID: z.string(),
40
+ CDP_API_KEY_SECRET: z.string(),
41
+ },
42
+ runtimeEnv: {
43
+ CDP_API_KEY_ID: process.env.CDP_API_KEY_ID,
44
+ CDP_API_KEY_SECRET: process.env.CDP_API_KEY_SECRET,
45
+ },
46
+ });
47
+ ```
48
+
49
+ Without these keys, x402 routes will fail to initialize (empty 402 responses, no payment header).
50
+
21
51
  ## Quick Start
22
52
 
23
53
  ### 1. Create the router (once per service)
package/dist/index.cjs CHANGED
@@ -84,9 +84,15 @@ module.exports = __toCommonJS(index_exports);
84
84
  // src/registry.ts
85
85
  var RouteRegistry = class {
86
86
  routes = /* @__PURE__ */ new Map();
87
+ // Silently overwrites on duplicate key. Next.js module loading order is
88
+ // non-deterministic during build — discovery stubs and real handlers may
89
+ // register the same route key in either order. Last writer wins.
90
+ // Prior art: ElysiaJS uses the same pattern (silent overwrite in router.history).
87
91
  register(entry) {
88
- if (this.routes.has(entry.key)) {
89
- throw new Error(`route '${entry.key}': already registered (duplicate route key)`);
92
+ if (this.routes.has(entry.key) && typeof process !== "undefined" && process.env?.["NODE_ENV"] !== "production") {
93
+ console.warn(
94
+ `[agentcash/router] route '${entry.key}' registered twice \u2014 overwriting (this is expected for discovery stubs during next build)`
95
+ );
90
96
  }
91
97
  this.routes.set(entry.key, entry);
92
98
  }
@@ -1172,6 +1178,23 @@ function createRouter(config) {
1172
1178
  const nonceStore = config.siwx?.nonceStore ?? new MemoryNonceStore();
1173
1179
  const network = config.network ?? "eip155:8453";
1174
1180
  const baseUrl = typeof globalThis.process !== "undefined" ? process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000" : "http://localhost:3000";
1181
+ if (config.protocols) {
1182
+ if (config.protocols.length === 0) {
1183
+ throw new Error(
1184
+ "RouterConfig.protocols cannot be empty. Omit the field to use default ['x402'] or specify protocols explicitly."
1185
+ );
1186
+ }
1187
+ if (config.protocols.includes("mpp") && !config.mpp) {
1188
+ throw new Error(
1189
+ 'RouterConfig.protocols includes "mpp" but RouterConfig.mpp is not configured. Add mpp: { secretKey, currency, recipient } to your router config.'
1190
+ );
1191
+ }
1192
+ if (config.protocols.includes("x402") && !config.payeeAddress) {
1193
+ throw new Error(
1194
+ 'RouterConfig.protocols includes "x402" but RouterConfig.payeeAddress is not configured.'
1195
+ );
1196
+ }
1197
+ }
1175
1198
  if (config.plugin?.init) {
1176
1199
  try {
1177
1200
  const result = config.plugin.init({ origin: baseUrl });
@@ -1207,7 +1230,8 @@ function createRouter(config) {
1207
1230
  route(key) {
1208
1231
  const builder = new RouteBuilder(key, registry, deps);
1209
1232
  if (config.prices && key in config.prices) {
1210
- return builder.paid(config.prices[key]);
1233
+ const options = config.protocols ? { protocols: config.protocols } : void 0;
1234
+ return builder.paid(config.prices[key], options);
1211
1235
  }
1212
1236
  return builder;
1213
1237
  },
package/dist/index.d.cts CHANGED
@@ -184,6 +184,20 @@ interface RouterConfig {
184
184
  currency: string;
185
185
  recipient?: string;
186
186
  };
187
+ /**
188
+ * Payment protocols to accept on auto-priced routes (those using the `prices` config).
189
+ *
190
+ * @default ['x402']
191
+ *
192
+ * @example
193
+ * // Accept both x402 and MPP payments
194
+ * createRouter({
195
+ * protocols: ['x402', 'mpp'],
196
+ * mpp: { secretKey, currency, recipient },
197
+ * prices: { 'exa/search': '0.01' }
198
+ * })
199
+ */
200
+ protocols?: ProtocolType[];
187
201
  }
188
202
 
189
203
  declare class RouteRegistry {
package/dist/index.d.ts CHANGED
@@ -184,6 +184,20 @@ interface RouterConfig {
184
184
  currency: string;
185
185
  recipient?: string;
186
186
  };
187
+ /**
188
+ * Payment protocols to accept on auto-priced routes (those using the `prices` config).
189
+ *
190
+ * @default ['x402']
191
+ *
192
+ * @example
193
+ * // Accept both x402 and MPP payments
194
+ * createRouter({
195
+ * protocols: ['x402', 'mpp'],
196
+ * mpp: { secretKey, currency, recipient },
197
+ * prices: { 'exa/search': '0.01' }
198
+ * })
199
+ */
200
+ protocols?: ProtocolType[];
187
201
  }
188
202
 
189
203
  declare class RouteRegistry {
package/dist/index.js CHANGED
@@ -50,9 +50,15 @@ var init_server = __esm({
50
50
  // src/registry.ts
51
51
  var RouteRegistry = class {
52
52
  routes = /* @__PURE__ */ new Map();
53
+ // Silently overwrites on duplicate key. Next.js module loading order is
54
+ // non-deterministic during build — discovery stubs and real handlers may
55
+ // register the same route key in either order. Last writer wins.
56
+ // Prior art: ElysiaJS uses the same pattern (silent overwrite in router.history).
53
57
  register(entry) {
54
- if (this.routes.has(entry.key)) {
55
- throw new Error(`route '${entry.key}': already registered (duplicate route key)`);
58
+ if (this.routes.has(entry.key) && typeof process !== "undefined" && process.env?.["NODE_ENV"] !== "production") {
59
+ console.warn(
60
+ `[agentcash/router] route '${entry.key}' registered twice \u2014 overwriting (this is expected for discovery stubs during next build)`
61
+ );
56
62
  }
57
63
  this.routes.set(entry.key, entry);
58
64
  }
@@ -1138,6 +1144,23 @@ function createRouter(config) {
1138
1144
  const nonceStore = config.siwx?.nonceStore ?? new MemoryNonceStore();
1139
1145
  const network = config.network ?? "eip155:8453";
1140
1146
  const baseUrl = typeof globalThis.process !== "undefined" ? process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000" : "http://localhost:3000";
1147
+ if (config.protocols) {
1148
+ if (config.protocols.length === 0) {
1149
+ throw new Error(
1150
+ "RouterConfig.protocols cannot be empty. Omit the field to use default ['x402'] or specify protocols explicitly."
1151
+ );
1152
+ }
1153
+ if (config.protocols.includes("mpp") && !config.mpp) {
1154
+ throw new Error(
1155
+ 'RouterConfig.protocols includes "mpp" but RouterConfig.mpp is not configured. Add mpp: { secretKey, currency, recipient } to your router config.'
1156
+ );
1157
+ }
1158
+ if (config.protocols.includes("x402") && !config.payeeAddress) {
1159
+ throw new Error(
1160
+ 'RouterConfig.protocols includes "x402" but RouterConfig.payeeAddress is not configured.'
1161
+ );
1162
+ }
1163
+ }
1141
1164
  if (config.plugin?.init) {
1142
1165
  try {
1143
1166
  const result = config.plugin.init({ origin: baseUrl });
@@ -1173,7 +1196,8 @@ function createRouter(config) {
1173
1196
  route(key) {
1174
1197
  const builder = new RouteBuilder(key, registry, deps);
1175
1198
  if (config.prices && key in config.prices) {
1176
- return builder.paid(config.prices[key]);
1199
+ const options = config.protocols ? { protocols: config.protocols } : void 0;
1200
+ return builder.paid(config.prices[key], options);
1177
1201
  }
1178
1202
  return builder;
1179
1203
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/router",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Unified route builder for Next.js App Router APIs with x402, MPP, SIWX, and API key auth",
5
5
  "type": "module",
6
6
  "exports": {