@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 +30 -0
- package/dist/index.cjs +27 -3
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +27 -3
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
},
|