@agentcash/router 1.9.4 → 1.10.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
@@ -119,6 +119,7 @@ export const POST = router.route({ path: 'search' })
119
119
  // app/api/inbox/status/route.ts
120
120
  export const GET = router.route({ path: 'inbox/status' })
121
121
  .siwx()
122
+ .method('GET')
122
123
  .handler(async ({ wallet }) => getStatus(wallet));
123
124
  ```
124
125
 
package/dist/index.cjs CHANGED
@@ -2177,6 +2177,21 @@ function getAllowedStrategies(allowed) {
2177
2177
  return allowed.map((name) => STRATEGIES[name]);
2178
2178
  }
2179
2179
 
2180
+ // src/protocols/detect.ts
2181
+ function detectProtocol(request) {
2182
+ if (request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)) {
2183
+ return "x402";
2184
+ }
2185
+ const auth = request.headers.get(HEADERS.AUTHORIZATION);
2186
+ if (auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT)) {
2187
+ return "mpp";
2188
+ }
2189
+ if (request.headers.get(HEADERS.SIWX)) {
2190
+ return "siwx";
2191
+ }
2192
+ return null;
2193
+ }
2194
+
2180
2195
  // src/pipeline/flows/api-key-only.ts
2181
2196
  async function runApiKeyOnlyFlow(ctx) {
2182
2197
  if (!ctx.routeEntry.apiKeyResolver) {
@@ -2192,6 +2207,11 @@ async function runApiKeyOnlyFlow(ctx) {
2192
2207
  var DynamicPricing = class {
2193
2208
  constructor(opts) {
2194
2209
  this.opts = opts;
2210
+ if (!isPositiveDecimal(opts.maxPrice)) {
2211
+ throw new Error(
2212
+ `route '${opts.route ?? "unknown"}': dynamic pricing requires a positive maxPrice, got '${opts.maxPrice}'`
2213
+ );
2214
+ }
2195
2215
  }
2196
2216
  needsBody = true;
2197
2217
  async quote(body) {
@@ -2205,11 +2225,8 @@ var DynamicPricing = class {
2205
2225
  error: err instanceof Error ? err.stack : String(err),
2206
2226
  body
2207
2227
  });
2208
- if (this.opts.maxPrice) {
2209
- this.alert("warn", `Using maxPrice ${this.opts.maxPrice} as fallback after pricing error`);
2210
- return this.opts.maxPrice;
2211
- }
2212
- throw err;
2228
+ this.alert("warn", `Using maxPrice ${this.opts.maxPrice} as fallback after pricing error`);
2229
+ return this.opts.maxPrice;
2213
2230
  }
2214
2231
  if (!isPositiveDecimal(priced)) {
2215
2232
  throw new HttpError(
@@ -2220,18 +2237,17 @@ var DynamicPricing = class {
2220
2237
  return priced;
2221
2238
  }
2222
2239
  challengeQuote(body) {
2223
- if (body === void 0) return Promise.resolve(this.opts.maxPrice ?? "0");
2240
+ if (body === void 0) return Promise.resolve(this.opts.maxPrice);
2224
2241
  return this.quote(body);
2225
2242
  }
2226
2243
  describe() {
2227
2244
  return {
2228
2245
  mode: "dynamic",
2229
2246
  min: this.opts.minPrice ?? "0",
2230
- max: this.opts.maxPrice ?? "0"
2247
+ max: this.opts.maxPrice
2231
2248
  };
2232
2249
  }
2233
2250
  cap(raw, body) {
2234
- if (!this.opts.maxPrice) return raw;
2235
2251
  let overCap;
2236
2252
  try {
2237
2253
  overCap = compareDecimals(raw, this.opts.maxPrice) > 0;
@@ -2331,6 +2347,9 @@ function selectPricing(raw, deps = {}) {
2331
2347
  return new FixedPricing(raw);
2332
2348
  }
2333
2349
  if (typeof raw === "function") {
2350
+ if (!deps.maxPrice) {
2351
+ throw new Error(`route '${deps.route ?? "unknown"}': dynamic pricing requires maxPrice`);
2352
+ }
2334
2353
  return new DynamicPricing({
2335
2354
  fn: raw,
2336
2355
  maxPrice: deps.maxPrice,
@@ -3215,21 +3234,6 @@ async function createKvMppStore(kv, options) {
3215
3234
  });
3216
3235
  }
3217
3236
 
3218
- // src/protocols/detect.ts
3219
- function detectProtocol(request) {
3220
- if (request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)) {
3221
- return "x402";
3222
- }
3223
- const auth = request.headers.get(HEADERS.AUTHORIZATION);
3224
- if (auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT)) {
3225
- return "mpp";
3226
- }
3227
- if (request.headers.get(HEADERS.SIWX)) {
3228
- return "siwx";
3229
- }
3230
- return null;
3231
- }
3232
-
3233
3237
  // src/protocols/mpp/siwx-mode.ts
3234
3238
  var import_mppx3 = require("mppx");
3235
3239
  async function verifyMppSiwx(request, mppx) {
@@ -3252,8 +3256,6 @@ async function runSiwxOnlyFlow(ctx) {
3252
3256
  if (earlyBody.ok) {
3253
3257
  const validateErr = await runValidate(ctx, earlyBody.data);
3254
3258
  if (validateErr) return validateErr;
3255
- } else {
3256
- return earlyBody.response;
3257
3259
  }
3258
3260
  }
3259
3261
  const siwxHeader = request.headers.get(HEADERS.SIWX);
@@ -3386,9 +3388,13 @@ async function runUnprotectedFlow(ctx) {
3386
3388
 
3387
3389
  // src/pipeline/orchestrate.ts
3388
3390
  function shouldSkipQueryValidation(routeEntry, request) {
3389
- const isPaidRoute = !!routeEntry.pricing || routeEntry.authMode === "paid";
3390
- if (!isPaidRoute) return false;
3391
- return selectIncomingStrategy(request, routeEntry.protocols) === null;
3391
+ if (routeEntry.pricing || routeEntry.authMode === "paid") {
3392
+ return selectIncomingStrategy(request, routeEntry.protocols) === null;
3393
+ }
3394
+ if (routeEntry.authMode === "siwx") {
3395
+ return !request.headers.get(HEADERS.SIWX) && detectProtocol(request) !== "mpp";
3396
+ }
3397
+ return false;
3392
3398
  }
3393
3399
  function createRequestHandler(routeEntry, handler, deps) {
3394
3400
  return async (request) => {
@@ -3582,6 +3588,11 @@ var RouteBuilder = class _RouteBuilder {
3582
3588
  `route '${this.#s.key}': price '${pricing}' must be a positive decimal string`
3583
3589
  );
3584
3590
  }
3591
+ if (typeof pricing === "function" && next.#s.maxPrice === void 0) {
3592
+ throw new Error(
3593
+ `route '${this.#s.key}': dynamic pricing requires maxPrice \u2014 without it, bare probes would advertise a $0 challenge`
3594
+ );
3595
+ }
3585
3596
  if (next.#s.maxPrice !== void 0 && !isPositiveDecimal(next.#s.maxPrice)) {
3586
3597
  throw new Error(
3587
3598
  `route '${this.#s.key}': maxPrice '${next.#s.maxPrice}' must be a positive decimal string`
package/dist/index.js CHANGED
@@ -2136,6 +2136,21 @@ function getAllowedStrategies(allowed) {
2136
2136
  return allowed.map((name) => STRATEGIES[name]);
2137
2137
  }
2138
2138
 
2139
+ // src/protocols/detect.ts
2140
+ function detectProtocol(request) {
2141
+ if (request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)) {
2142
+ return "x402";
2143
+ }
2144
+ const auth = request.headers.get(HEADERS.AUTHORIZATION);
2145
+ if (auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT)) {
2146
+ return "mpp";
2147
+ }
2148
+ if (request.headers.get(HEADERS.SIWX)) {
2149
+ return "siwx";
2150
+ }
2151
+ return null;
2152
+ }
2153
+
2139
2154
  // src/pipeline/flows/api-key-only.ts
2140
2155
  async function runApiKeyOnlyFlow(ctx) {
2141
2156
  if (!ctx.routeEntry.apiKeyResolver) {
@@ -2151,6 +2166,11 @@ async function runApiKeyOnlyFlow(ctx) {
2151
2166
  var DynamicPricing = class {
2152
2167
  constructor(opts) {
2153
2168
  this.opts = opts;
2169
+ if (!isPositiveDecimal(opts.maxPrice)) {
2170
+ throw new Error(
2171
+ `route '${opts.route ?? "unknown"}': dynamic pricing requires a positive maxPrice, got '${opts.maxPrice}'`
2172
+ );
2173
+ }
2154
2174
  }
2155
2175
  needsBody = true;
2156
2176
  async quote(body) {
@@ -2164,11 +2184,8 @@ var DynamicPricing = class {
2164
2184
  error: err instanceof Error ? err.stack : String(err),
2165
2185
  body
2166
2186
  });
2167
- if (this.opts.maxPrice) {
2168
- this.alert("warn", `Using maxPrice ${this.opts.maxPrice} as fallback after pricing error`);
2169
- return this.opts.maxPrice;
2170
- }
2171
- throw err;
2187
+ this.alert("warn", `Using maxPrice ${this.opts.maxPrice} as fallback after pricing error`);
2188
+ return this.opts.maxPrice;
2172
2189
  }
2173
2190
  if (!isPositiveDecimal(priced)) {
2174
2191
  throw new HttpError(
@@ -2179,18 +2196,17 @@ var DynamicPricing = class {
2179
2196
  return priced;
2180
2197
  }
2181
2198
  challengeQuote(body) {
2182
- if (body === void 0) return Promise.resolve(this.opts.maxPrice ?? "0");
2199
+ if (body === void 0) return Promise.resolve(this.opts.maxPrice);
2183
2200
  return this.quote(body);
2184
2201
  }
2185
2202
  describe() {
2186
2203
  return {
2187
2204
  mode: "dynamic",
2188
2205
  min: this.opts.minPrice ?? "0",
2189
- max: this.opts.maxPrice ?? "0"
2206
+ max: this.opts.maxPrice
2190
2207
  };
2191
2208
  }
2192
2209
  cap(raw, body) {
2193
- if (!this.opts.maxPrice) return raw;
2194
2210
  let overCap;
2195
2211
  try {
2196
2212
  overCap = compareDecimals(raw, this.opts.maxPrice) > 0;
@@ -2290,6 +2306,9 @@ function selectPricing(raw, deps = {}) {
2290
2306
  return new FixedPricing(raw);
2291
2307
  }
2292
2308
  if (typeof raw === "function") {
2309
+ if (!deps.maxPrice) {
2310
+ throw new Error(`route '${deps.route ?? "unknown"}': dynamic pricing requires maxPrice`);
2311
+ }
2293
2312
  return new DynamicPricing({
2294
2313
  fn: raw,
2295
2314
  maxPrice: deps.maxPrice,
@@ -3174,21 +3193,6 @@ async function createKvMppStore(kv, options) {
3174
3193
  });
3175
3194
  }
3176
3195
 
3177
- // src/protocols/detect.ts
3178
- function detectProtocol(request) {
3179
- if (request.headers.get(HEADERS.X402_PAYMENT_SIGNATURE) ?? request.headers.get(HEADERS.X402_PAYMENT_LEGACY)) {
3180
- return "x402";
3181
- }
3182
- const auth = request.headers.get(HEADERS.AUTHORIZATION);
3183
- if (auth && auth.startsWith(AUTH_SCHEME.MPP_PAYMENT)) {
3184
- return "mpp";
3185
- }
3186
- if (request.headers.get(HEADERS.SIWX)) {
3187
- return "siwx";
3188
- }
3189
- return null;
3190
- }
3191
-
3192
3196
  // src/protocols/mpp/siwx-mode.ts
3193
3197
  import { Credential as Credential2 } from "mppx";
3194
3198
  async function verifyMppSiwx(request, mppx) {
@@ -3211,8 +3215,6 @@ async function runSiwxOnlyFlow(ctx) {
3211
3215
  if (earlyBody.ok) {
3212
3216
  const validateErr = await runValidate(ctx, earlyBody.data);
3213
3217
  if (validateErr) return validateErr;
3214
- } else {
3215
- return earlyBody.response;
3216
3218
  }
3217
3219
  }
3218
3220
  const siwxHeader = request.headers.get(HEADERS.SIWX);
@@ -3345,9 +3347,13 @@ async function runUnprotectedFlow(ctx) {
3345
3347
 
3346
3348
  // src/pipeline/orchestrate.ts
3347
3349
  function shouldSkipQueryValidation(routeEntry, request) {
3348
- const isPaidRoute = !!routeEntry.pricing || routeEntry.authMode === "paid";
3349
- if (!isPaidRoute) return false;
3350
- return selectIncomingStrategy(request, routeEntry.protocols) === null;
3350
+ if (routeEntry.pricing || routeEntry.authMode === "paid") {
3351
+ return selectIncomingStrategy(request, routeEntry.protocols) === null;
3352
+ }
3353
+ if (routeEntry.authMode === "siwx") {
3354
+ return !request.headers.get(HEADERS.SIWX) && detectProtocol(request) !== "mpp";
3355
+ }
3356
+ return false;
3351
3357
  }
3352
3358
  function createRequestHandler(routeEntry, handler, deps) {
3353
3359
  return async (request) => {
@@ -3541,6 +3547,11 @@ var RouteBuilder = class _RouteBuilder {
3541
3547
  `route '${this.#s.key}': price '${pricing}' must be a positive decimal string`
3542
3548
  );
3543
3549
  }
3550
+ if (typeof pricing === "function" && next.#s.maxPrice === void 0) {
3551
+ throw new Error(
3552
+ `route '${this.#s.key}': dynamic pricing requires maxPrice \u2014 without it, bare probes would advertise a $0 challenge`
3553
+ );
3554
+ }
3544
3555
  if (next.#s.maxPrice !== void 0 && !isPositiveDecimal(next.#s.maxPrice)) {
3545
3556
  throw new Error(
3546
3557
  `route '${this.#s.key}': maxPrice '${next.#s.maxPrice}' must be a positive decimal string`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/router",
3
- "version": "1.9.4",
3
+ "version": "1.10.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": {