@agentcash/router 1.7.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
@@ -242,12 +242,139 @@ var init_facilitators = __esm({
242
242
  }
243
243
  });
244
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();
369
+ }
370
+ });
371
+
245
372
  // src/init/x402-server.ts
246
373
  var x402_server_exports = {};
247
374
  __export(x402_server_exports, {
248
375
  createX402Server: () => createX402Server
249
376
  });
250
- async function createX402Server(config) {
377
+ async function createX402Server(config, kvStore) {
251
378
  const { x402ResourceServer, HTTPFacilitatorClient } = await import("@x402/core/server");
252
379
  const { registerExactEvmScheme } = await import("@x402/evm/exact/server");
253
380
  const { bazaarResourceServerExtension } = await import("@x402/extensions/bazaar");
@@ -261,7 +388,11 @@ async function createX402Server(config) {
261
388
  );
262
389
  const evmNetworks = filterEvmNetworks(configuredNetworks);
263
390
  const svmNetworks = filterSolanaNetworks(configuredNetworks);
264
- const facilitatorClients = createFacilitatorClients(facilitatorsByNetwork, HTTPFacilitatorClient);
391
+ const facilitatorClients = createFacilitatorClients(
392
+ facilitatorsByNetwork,
393
+ HTTPFacilitatorClient,
394
+ kvStore
395
+ );
265
396
  const server = new x402ResourceServer(
266
397
  facilitatorClients.length === 1 ? facilitatorClients[0] : facilitatorClients
267
398
  );
@@ -285,48 +416,13 @@ async function createX402Server(config) {
285
416
  facilitatorsByNetwork
286
417
  };
287
418
  }
288
- function createFacilitatorClients(facilitatorsByNetwork, HTTPFacilitatorClient) {
289
- const groups = getResolvedX402FacilitatorGroups(facilitatorsByNetwork);
290
- return groups.map((group) => {
291
- const inner = new HTTPFacilitatorClient(group.config);
292
- const kinds = buildSupportedKinds(group);
293
- return hardcodedSupportedClient(inner, kinds);
294
- });
295
- }
296
- function hardcodedSupportedClient(inner, kinds) {
297
- return {
298
- verify: inner.verify.bind(inner),
299
- settle: inner.settle.bind(inner),
300
- getSupported: async () => ({ kinds, extensions: [], signers: {} })
301
- };
302
- }
303
- function buildSupportedKinds(group) {
304
- return group.networks.flatMap((network) => {
305
- const exactKind = {
306
- x402Version: 2,
307
- scheme: "exact",
308
- network,
309
- ...group.family === "solana" ? {
310
- extra: {
311
- features: {
312
- xSettlementAccountSupported: true
313
- }
314
- }
315
- } : {}
316
- };
317
- const uptoKind = { x402Version: 2, scheme: "upto", network };
318
- if (group.family === "evm") {
319
- return [exactKind, uptoKind];
320
- }
321
- return [exactKind, uptoKind];
322
- });
323
- }
324
419
  var init_x402_server = __esm({
325
420
  "src/init/x402-server.ts"() {
326
421
  "use strict";
327
422
  init_evm();
328
423
  init_solana();
329
424
  init_facilitators();
425
+ init_facilitator_clients();
330
426
  init_accepts();
331
427
  }
332
428
  });
@@ -1028,6 +1124,61 @@ async function runApiKeyOnlyFlow(ctx) {
1028
1124
  return runHandlerOnly(ctx, null, result.account);
1029
1125
  }
1030
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
+
1031
1182
  // src/pricing/dynamic.ts
1032
1183
  var DynamicPricing = class {
1033
1184
  constructor(opts) {
@@ -1063,9 +1214,13 @@ var DynamicPricing = class {
1063
1214
  }
1064
1215
  cap(raw, body) {
1065
1216
  if (!this.opts.maxPrice) return raw;
1066
- const n = parseFloat(raw);
1067
- const max = parseFloat(this.opts.maxPrice);
1068
- 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) {
1069
1224
  this.alert("warn", `Price ${raw} exceeds maxPrice ${this.opts.maxPrice}, capping`, {
1070
1225
  calculated: raw,
1071
1226
  maxPrice: this.opts.maxPrice,
@@ -1142,7 +1297,7 @@ var TieredPricing = class {
1142
1297
  maxTierPrice() {
1143
1298
  let max = "0";
1144
1299
  for (const tier of Object.values(this.opts.tiers)) {
1145
- if (parseFloat(tier.price) > parseFloat(max)) max = tier.price;
1300
+ if (compareDecimals(tier.price, max) > 0) max = tier.price;
1146
1301
  }
1147
1302
  return max;
1148
1303
  }
@@ -1620,17 +1775,6 @@ var mppStrategy = {
1620
1775
  return buildChargeChallenge(args);
1621
1776
  }
1622
1777
  };
1623
- function multiplyDecimal(decimal, factor) {
1624
- if (!Number.isFinite(factor) || factor <= 0) return decimal;
1625
- const [whole, fraction = ""] = decimal.split(".");
1626
- const scaled = (BigInt(whole + fraction) * BigInt(factor)).toString();
1627
- const decimals = fraction.length;
1628
- if (decimals === 0) return scaled;
1629
- const padded = scaled.padStart(decimals + 1, "0");
1630
- const intPart = padded.slice(0, padded.length - decimals);
1631
- const fracPart = padded.slice(padded.length - decimals).replace(/0+$/, "");
1632
- return fracPart ? `${intPart}.${fracPart}` : intPart;
1633
- }
1634
1778
  async function buildChargeChallenge(args) {
1635
1779
  if (!args.deps.mppx) return {};
1636
1780
  try {
@@ -1723,26 +1867,13 @@ function buildCustomRequirement(price, accept) {
1723
1867
  return {
1724
1868
  scheme: accept.scheme,
1725
1869
  network: accept.network,
1726
- amount: decimalToAtomicUnits(price, accept.decimals ?? 6),
1870
+ amount: decimalToAtomic(price, accept.decimals ?? 6).toString(),
1727
1871
  asset: accept.asset,
1728
1872
  payTo: accept.payTo,
1729
1873
  maxTimeoutSeconds: accept.maxTimeoutSeconds ?? 300,
1730
1874
  extra: accept.extra ?? {}
1731
1875
  };
1732
1876
  }
1733
- function decimalToAtomicUnits(amount, decimals) {
1734
- const match = /^(?<whole>\d+)(?:\.(?<fraction>\d+))?$/.exec(amount);
1735
- if (!match?.groups) {
1736
- throw new Error(`Invalid decimal amount '${amount}'`);
1737
- }
1738
- const whole = match.groups.whole;
1739
- const fraction = match.groups.fraction ?? "";
1740
- if (fraction.length > decimals) {
1741
- throw new Error(`Amount '${amount}' exceeds ${decimals} decimal places`);
1742
- }
1743
- const normalized = `${whole}${fraction.padEnd(decimals, "0")}`.replace(/^0+(?=\d)/, "");
1744
- return normalized === "" ? "0" : normalized;
1745
- }
1746
1877
 
1747
1878
  // src/protocols/x402/challenge.ts
1748
1879
  async function buildX402Challenge(opts) {
@@ -2105,13 +2236,14 @@ async function buildX402ChallengeContribution(args) {
2105
2236
  }
2106
2237
  function reportSettleFailure(report, err, network) {
2107
2238
  const facilitator = err ?? {};
2108
- report("error", "Settlement failed", {
2239
+ const meta = {
2109
2240
  error: err instanceof Error ? err.message : String(err),
2110
2241
  network,
2111
2242
  errorReason: facilitator.errorReason,
2112
2243
  facilitatorStatus: facilitator.response?.status,
2113
2244
  facilitatorBody: facilitator.response?.data ?? facilitator.response?.body
2114
- });
2245
+ };
2246
+ report("error", "Settlement failed", meta);
2115
2247
  }
2116
2248
 
2117
2249
  // src/protocols/index.ts
@@ -2134,6 +2266,7 @@ function getAllowedStrategies(allowed) {
2134
2266
  import { NextResponse as NextResponse4 } from "next/server";
2135
2267
 
2136
2268
  // src/pipeline/challenge-extensions.ts
2269
+ init_evm();
2137
2270
  async function buildChallengeExtensions(ctx) {
2138
2271
  const { routeEntry } = ctx;
2139
2272
  let extensions;
@@ -2178,6 +2311,23 @@ async function buildChallengeExtensions(ctx) {
2178
2311
  } catch {
2179
2312
  }
2180
2313
  }
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
+ }
2181
2331
  return extensions;
2182
2332
  }
2183
2333
 
@@ -2333,27 +2483,6 @@ async function runDynamicChannelMgmtFlow(args) {
2333
2483
  // src/pipeline/flows/dynamic/dynamic-invoke.ts
2334
2484
  import { NextResponse as NextResponse6 } from "next/server";
2335
2485
 
2336
- // src/pricing/atomic.ts
2337
- var USDC_DECIMALS = 6;
2338
- function decimalToAtomic(amount) {
2339
- const m = /^(\d+)(?:\.(\d+))?$/.exec(amount.trim());
2340
- if (!m) {
2341
- throw Object.assign(new Error(`'${amount}' is not a valid decimal-dollar string`), {
2342
- status: 400
2343
- });
2344
- }
2345
- const whole = m[1];
2346
- const fraction = (m[2] ?? "").slice(0, USDC_DECIMALS).padEnd(USDC_DECIMALS, "0");
2347
- return BigInt(`${whole}${fraction}`.replace(/^0+(?=\d)/, "") || "0");
2348
- }
2349
- function atomicToDecimal(atomic) {
2350
- const whole = atomic / 10n ** BigInt(USDC_DECIMALS);
2351
- const fraction = atomic % 10n ** BigInt(USDC_DECIMALS);
2352
- if (fraction === 0n) return whole.toString();
2353
- const fractionStr = fraction.toString().padStart(USDC_DECIMALS, "0").replace(/0+$/, "");
2354
- return `${whole}.${fractionStr}`;
2355
- }
2356
-
2357
2486
  // src/pricing/charge-context.ts
2358
2487
  function createChargeContext(args) {
2359
2488
  const { tickCost, maxPrice, route } = args;
@@ -2764,6 +2893,19 @@ async function runPaidFlow(ctx) {
2764
2893
  import { NextResponse as NextResponse7 } from "next/server";
2765
2894
 
2766
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
+ }
2767
2909
  function restKvStore(url, token) {
2768
2910
  const base = url.replace(/\/+$/, "");
2769
2911
  const authHeader = { Authorization: `Bearer ${token}` };
@@ -2785,16 +2927,22 @@ function restKvStore(url, token) {
2785
2927
  const res = await fetch(`${base}/get/${encodeURIComponent(key)}`, { headers: authHeader });
2786
2928
  if (!res.ok) throw new Error(`[kv-store] GET ${key}: ${res.status}`);
2787
2929
  const { result } = await res.json();
2788
- 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
+ }
2789
2937
  }
2790
2938
  async function set(key, value) {
2791
- await exec(["SET", key, JSON.stringify(value)]);
2939
+ await exec(["SET", key, stringifyValue(value)]);
2792
2940
  }
2793
2941
  async function del(key) {
2794
2942
  await exec(["DEL", key]);
2795
2943
  }
2796
2944
  async function setNxEx(key, value, ttlSeconds) {
2797
- const result = await exec(["SET", key, JSON.stringify(value), "EX", ttlSeconds, "NX"]);
2945
+ const result = await exec(["SET", key, stringifyValue(value), "EX", ttlSeconds, "NX"]);
2798
2946
  return result === "OK";
2799
2947
  }
2800
2948
  async function sadd(key, member) {
@@ -3123,142 +3271,109 @@ ${issues}`
3123
3271
  }
3124
3272
 
3125
3273
  // src/builder.ts
3126
- var RouteBuilder = class {
3127
- /** @internal */
3128
- _key;
3129
- /** @internal */
3130
- _registry;
3131
- /** @internal */
3132
- _deps;
3133
- /** @internal */
3134
- _authMode = null;
3135
- /** @internal */
3136
- _pricing;
3137
- /** @internal */
3138
- _siwxEnabled = false;
3139
- /** @internal */
3140
- _protocols = ["x402"];
3141
- /** @internal */
3142
- _maxPrice;
3143
- /** @internal */
3144
- _minPrice;
3145
- /** @internal */
3146
- _dynamicPrice = false;
3147
- /** @internal */
3148
- _tickCost;
3149
- /** @internal */
3150
- _unitType;
3151
- /** @internal */
3152
- _payTo;
3153
- /** @internal */
3154
- _bodySchema;
3155
- /** @internal */
3156
- _querySchema;
3157
- /** @internal */
3158
- _outputSchema;
3159
- /** @internal */
3160
- _inputExample = void 0;
3161
- /** @internal */
3162
- _hasInputExample = false;
3163
- /** @internal */
3164
- _outputExample = void 0;
3165
- /** @internal */
3166
- _hasOutputExample = false;
3167
- /** @internal */
3168
- _description;
3169
- /** @internal */
3170
- _path;
3171
- /** @internal */
3172
- _method = "POST";
3173
- /** @internal */
3174
- _apiKeyResolver;
3175
- /** @internal */
3176
- _providerName;
3177
- /** @internal */
3178
- _providerConfig;
3179
- /** @internal */
3180
- _validateFn;
3181
- /** @internal */
3182
- _settlement;
3183
- /** @internal */
3184
- _mppInfo;
3185
- constructor(key, registry, deps) {
3186
- this._key = key;
3187
- this._registry = registry;
3188
- 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
+ };
3189
3308
  }
3190
3309
  fork() {
3191
- const next = Object.create(Object.getPrototypeOf(this));
3192
- Object.assign(next, this);
3193
- 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] };
3194
3316
  return next;
3195
3317
  }
3196
3318
  paid(pricingOrOptions, options) {
3197
- const { pricing, resolvedOptions } = resolvePaidArgs(this._key, pricingOrOptions, options);
3198
- if (this._authMode === "unprotected") {
3319
+ const { pricing, resolvedOptions } = resolvePaidArgs(this.#s.key, pricingOrOptions, options);
3320
+ if (this.#s.authMode === "unprotected") {
3199
3321
  throw new Error(
3200
- `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.`
3201
3323
  );
3202
3324
  }
3203
- if (this._pricing !== void 0) {
3325
+ if (this.#s.pricing !== void 0) {
3204
3326
  throw new Error(
3205
- `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.`
3206
3328
  );
3207
3329
  }
3208
3330
  const next = this.fork();
3209
- next._authMode = "paid";
3210
- next._pricing = pricing;
3331
+ next.#s.authMode = "paid";
3332
+ next.#s.pricing = pricing;
3211
3333
  if (resolvedOptions?.protocols) {
3212
- next._protocols = [...resolvedOptions.protocols];
3213
- } else if (next._protocols.length === 0) {
3214
- next._protocols = ["x402"];
3334
+ next.#s.protocols = [...resolvedOptions.protocols];
3335
+ } else if (next.#s.protocols.length === 0) {
3336
+ next.#s.protocols = ["x402"];
3215
3337
  }
3216
- if (resolvedOptions?.maxPrice) next._maxPrice = resolvedOptions.maxPrice;
3217
- if (resolvedOptions?.minPrice) next._minPrice = resolvedOptions.minPrice;
3218
- if (resolvedOptions?.payTo) next._payTo = resolvedOptions.payTo;
3219
- if (resolvedOptions?.mpp) next._mppInfo = resolvedOptions.mpp;
3220
- if (resolvedOptions?.dynamic) next._dynamicPrice = true;
3221
- if (resolvedOptions?.tickCost) next._tickCost = resolvedOptions.tickCost;
3222
- 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;
3223
3345
  if (typeof pricing === "object" && "tiers" in pricing) {
3224
- if (next._dynamicPrice) {
3346
+ if (next.#s.dynamicPrice) {
3225
3347
  throw new Error(
3226
- `route '${this._key}': .paid({ dynamic: true }) is incompatible with tiered pricing`
3348
+ `route '${this.#s.key}': .paid({ dynamic: true }) is incompatible with tiered pricing`
3227
3349
  );
3228
3350
  }
3229
3351
  for (const [tierKey, tierConfig] of Object.entries(pricing.tiers)) {
3230
3352
  if (!tierKey) {
3231
- throw new Error(`route '${this._key}': tier key cannot be empty`);
3353
+ throw new Error(`route '${this.#s.key}': tier key cannot be empty`);
3232
3354
  }
3233
- const tierPrice = parseFloat(tierConfig.price);
3234
- if (isNaN(tierPrice) || tierPrice <= 0) {
3355
+ if (!isPositiveDecimal(tierConfig.price)) {
3235
3356
  throw new Error(
3236
- `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`
3237
3358
  );
3238
3359
  }
3239
3360
  }
3240
3361
  }
3241
- if (resolvedOptions?.maxPrice !== void 0) {
3242
- const parsed = parseFloat(resolvedOptions.maxPrice);
3243
- if (isNaN(parsed) || parsed <= 0) {
3244
- throw new Error(
3245
- `route '${this._key}': maxPrice '${resolvedOptions.maxPrice}' must be a positive decimal string`
3246
- );
3247
- }
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
+ );
3248
3366
  }
3249
- if (resolvedOptions?.tickCost !== void 0) {
3250
- const parsed = parseFloat(resolvedOptions.tickCost);
3251
- if (isNaN(parsed) || parsed <= 0) {
3252
- throw new Error(
3253
- `route '${this._key}': tickCost '${resolvedOptions.tickCost}' must be a positive decimal string`
3254
- );
3255
- }
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
+ );
3256
3371
  }
3257
- if (next._dynamicPrice && !next._maxPrice) {
3258
- 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`);
3259
3374
  }
3260
- if (next._dynamicPrice && !next._tickCost) {
3261
- 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`);
3262
3377
  }
3263
3378
  return next;
3264
3379
  }
@@ -3273,25 +3388,25 @@ var RouteBuilder = class {
3273
3388
  * ```
3274
3389
  */
3275
3390
  siwx() {
3276
- if (this._authMode === "unprotected") {
3391
+ if (this.#s.authMode === "unprotected") {
3277
3392
  throw new Error(
3278
- `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.`
3279
3394
  );
3280
3395
  }
3281
- if (this._apiKeyResolver) {
3396
+ if (this.#s.apiKeyResolver) {
3282
3397
  throw new Error(
3283
- `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.`
3284
3399
  );
3285
3400
  }
3286
3401
  const next = this.fork();
3287
- next._siwxEnabled = true;
3288
- if (next._authMode === "paid" || next._pricing) {
3289
- next._authMode = "paid";
3290
- 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"];
3291
3406
  return next;
3292
3407
  }
3293
- next._authMode = "siwx";
3294
- next._protocols = [];
3408
+ next.#s.authMode = "siwx";
3409
+ next.#s.protocols = [];
3295
3410
  return next;
3296
3411
  }
3297
3412
  /**
@@ -3308,14 +3423,14 @@ var RouteBuilder = class {
3308
3423
  * ```
3309
3424
  */
3310
3425
  apiKey(resolver) {
3311
- if (this._siwxEnabled) {
3426
+ if (this.#s.siwxEnabled) {
3312
3427
  throw new Error(
3313
- `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.`
3314
3429
  );
3315
3430
  }
3316
3431
  const next = this.fork();
3317
- next._authMode = "apiKey";
3318
- next._apiKeyResolver = resolver;
3432
+ next.#s.authMode = "apiKey";
3433
+ next.#s.apiKeyResolver = resolver;
3319
3434
  return next;
3320
3435
  }
3321
3436
  /**
@@ -3328,19 +3443,19 @@ var RouteBuilder = class {
3328
3443
  * ```
3329
3444
  */
3330
3445
  unprotected() {
3331
- if (this._authMode && this._authMode !== "unprotected") {
3446
+ if (this.#s.authMode && this.#s.authMode !== "unprotected") {
3332
3447
  throw new Error(
3333
- `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.`
3334
3449
  );
3335
3450
  }
3336
- if (this._pricing) {
3451
+ if (this.#s.pricing) {
3337
3452
  throw new Error(
3338
- `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.`
3339
3454
  );
3340
3455
  }
3341
3456
  const next = this.fork();
3342
- next._authMode = "unprotected";
3343
- next._protocols = [];
3457
+ next.#s.authMode = "unprotected";
3458
+ next.#s.protocols = [];
3344
3459
  return next;
3345
3460
  }
3346
3461
  /**
@@ -3359,8 +3474,8 @@ var RouteBuilder = class {
3359
3474
  */
3360
3475
  provider(name, config) {
3361
3476
  const next = this.fork();
3362
- next._providerName = name;
3363
- next._providerConfig = config ?? {};
3477
+ next.#s.providerName = name;
3478
+ next.#s.providerConfig = config ?? {};
3364
3479
  return next;
3365
3480
  }
3366
3481
  /**
@@ -3375,7 +3490,7 @@ var RouteBuilder = class {
3375
3490
  */
3376
3491
  body(schema) {
3377
3492
  const next = this.fork();
3378
- next._bodySchema = schema;
3493
+ next.#s.bodySchema = schema;
3379
3494
  return next;
3380
3495
  }
3381
3496
  /**
@@ -3391,8 +3506,8 @@ var RouteBuilder = class {
3391
3506
  */
3392
3507
  query(schema) {
3393
3508
  const next = this.fork();
3394
- next._querySchema = schema;
3395
- next._method = "GET";
3509
+ next.#s.querySchema = schema;
3510
+ next.#s.method = "GET";
3396
3511
  return next;
3397
3512
  }
3398
3513
  /**
@@ -3409,7 +3524,7 @@ var RouteBuilder = class {
3409
3524
  */
3410
3525
  output(schema) {
3411
3526
  const next = this.fork();
3412
- next._outputSchema = schema;
3527
+ next.#s.outputSchema = schema;
3413
3528
  return next;
3414
3529
  }
3415
3530
  /**
@@ -3423,8 +3538,8 @@ var RouteBuilder = class {
3423
3538
  */
3424
3539
  inputExample(example) {
3425
3540
  const next = this.fork();
3426
- next._inputExample = example;
3427
- next._hasInputExample = true;
3541
+ next.#s.inputExample = example;
3542
+ next.#s.hasInputExample = true;
3428
3543
  return next;
3429
3544
  }
3430
3545
  /**
@@ -3438,8 +3553,8 @@ var RouteBuilder = class {
3438
3553
  */
3439
3554
  outputExample(example) {
3440
3555
  const next = this.fork();
3441
- next._outputExample = example;
3442
- next._hasOutputExample = true;
3556
+ next.#s.outputExample = example;
3557
+ next.#s.hasOutputExample = true;
3443
3558
  return next;
3444
3559
  }
3445
3560
  /**
@@ -3453,7 +3568,7 @@ var RouteBuilder = class {
3453
3568
  */
3454
3569
  description(text) {
3455
3570
  const next = this.fork();
3456
- next._description = text;
3571
+ next.#s.description = text;
3457
3572
  return next;
3458
3573
  }
3459
3574
  /**
@@ -3467,7 +3582,7 @@ var RouteBuilder = class {
3467
3582
  */
3468
3583
  path(p) {
3469
3584
  const next = this.fork();
3470
- next._path = p;
3585
+ next.#s.path = p;
3471
3586
  return next;
3472
3587
  }
3473
3588
  /**
@@ -3481,7 +3596,7 @@ var RouteBuilder = class {
3481
3596
  */
3482
3597
  method(m) {
3483
3598
  const next = this.fork();
3484
- next._method = m;
3599
+ next.#s.method = m;
3485
3600
  return next;
3486
3601
  }
3487
3602
  /**
@@ -3500,7 +3615,7 @@ var RouteBuilder = class {
3500
3615
  */
3501
3616
  validate(fn) {
3502
3617
  const next = this.fork();
3503
- next._validateFn = fn;
3618
+ next.#s.validateFn = fn;
3504
3619
  return next;
3505
3620
  }
3506
3621
  /**
@@ -3518,7 +3633,7 @@ var RouteBuilder = class {
3518
3633
  */
3519
3634
  settlement(lifecycle) {
3520
3635
  const next = this.fork();
3521
- next._settlement = lifecycle;
3636
+ next.#s.settlement = lifecycle;
3522
3637
  return next;
3523
3638
  }
3524
3639
  /**
@@ -3561,79 +3676,79 @@ var RouteBuilder = class {
3561
3676
  return this.register(fn, true);
3562
3677
  }
3563
3678
  register(handlerFn, streaming) {
3564
- if (!this._authMode) {
3679
+ if (!this.#s.authMode) {
3565
3680
  throw new Error(
3566
- `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()`
3567
3682
  );
3568
3683
  }
3569
- if (this._validateFn && !this._bodySchema) {
3684
+ if (this.#s.validateFn && !this.#s.bodySchema) {
3570
3685
  throw new Error(
3571
- `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`
3572
3687
  );
3573
3688
  }
3574
- if (this._settlement && !this._pricing) {
3575
- 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`);
3576
3691
  }
3577
- if (this._dynamicPrice && this._protocols.includes("x402")) {
3578
- 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");
3579
3694
  if (!hasUpto) {
3580
3695
  throw new Error(
3581
- `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.`
3582
3697
  );
3583
3698
  }
3584
3699
  }
3585
- if (this._dynamicPrice && this._protocols.includes("mpp")) {
3586
- if (!this._deps.mppSessionConfig) {
3700
+ if (this.#s.dynamicPrice && this.#s.protocols.includes("mpp")) {
3701
+ if (!this.#s.deps.mppSessionConfig) {
3587
3702
  throw new Error(
3588
- `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.`
3589
3704
  );
3590
3705
  }
3591
3706
  }
3592
- if (streaming && !this._dynamicPrice) {
3707
+ if (streaming && !this.#s.dynamicPrice) {
3593
3708
  throw new Error(
3594
- `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.`
3595
3710
  );
3596
3711
  }
3597
3712
  validateExamples(
3598
- this._key,
3599
- this._bodySchema,
3600
- this._querySchema,
3601
- this._outputSchema,
3602
- this._inputExample,
3603
- this._hasInputExample,
3604
- this._outputExample,
3605
- 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
3606
3721
  );
3607
3722
  const entry = {
3608
- key: this._key,
3609
- authMode: this._authMode,
3610
- siwxEnabled: this._siwxEnabled,
3611
- pricing: this._pricing,
3612
- 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,
3613
3728
  streaming: streaming ? true : void 0,
3614
- protocols: this._protocols,
3615
- bodySchema: this._bodySchema,
3616
- querySchema: this._querySchema,
3617
- outputSchema: this._outputSchema,
3618
- inputExample: this._hasInputExample ? this._inputExample : void 0,
3619
- outputExample: this._hasOutputExample ? this._outputExample : void 0,
3620
- description: this._description,
3621
- path: this._path,
3622
- method: this._method,
3623
- maxPrice: this._maxPrice,
3624
- minPrice: this._minPrice,
3625
- payTo: this._payTo,
3626
- apiKeyResolver: this._apiKeyResolver,
3627
- providerName: this._providerName,
3628
- providerConfig: this._providerConfig,
3629
- validateFn: this._validateFn,
3630
- settlement: this._settlement,
3631
- mppInfo: this._mppInfo,
3632
- tickCost: this._tickCost,
3633
- 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
3634
3749
  };
3635
- this._registry.register(entry);
3636
- return createRequestHandler(entry, handlerFn, this._deps);
3750
+ this.#s.registry.register(entry);
3751
+ return createRequestHandler(entry, handlerFn, this.#s.deps);
3637
3752
  }
3638
3753
  };
3639
3754
  function resolvePaidArgs(routeKey, pricingOrOptions, options) {
@@ -3877,17 +3992,16 @@ function buildPricingInfo(entry) {
3877
3992
  };
3878
3993
  }
3879
3994
  if ("tiers" in entry.pricing) {
3880
- const tierPrices = Object.values(entry.pricing.tiers).map((tier) => parseFloat(tier.price));
3881
- const min = Math.min(...tierPrices);
3882
- const max = Math.max(...tierPrices);
3883
- if (Number.isFinite(min) && Number.isFinite(max)) {
3884
- 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) {
3885
3999
  return {
3886
- price: { mode: "fixed", currency: "USD", amount: String(min) }
4000
+ price: { mode: "fixed", currency: "USD", amount: extrema.min }
3887
4001
  };
3888
4002
  }
3889
4003
  return {
3890
- price: { mode: "dynamic", currency: "USD", min: String(min), max: String(max) }
4004
+ price: { mode: "dynamic", currency: "USD", min: extrema.min, max: extrema.max }
3891
4005
  };
3892
4006
  }
3893
4007
  return {
@@ -3901,6 +4015,20 @@ function buildPricingInfo(entry) {
3901
4015
  }
3902
4016
  return void 0;
3903
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
+ }
3904
4032
 
3905
4033
  // src/discovery/llms-txt.ts
3906
4034
  import { NextResponse as NextResponse10 } from "next/server";
@@ -4387,11 +4515,11 @@ function getRouterConfigIssues(config, options = {}) {
4387
4515
  }
4388
4516
 
4389
4517
  // src/init/x402.ts
4390
- async function initX402(config, configError) {
4518
+ async function initX402(config, kvStore, configError) {
4391
4519
  if (configError) return { initError: configError };
4392
4520
  try {
4393
4521
  const { createX402Server: createX402Server2 } = await Promise.resolve().then(() => (init_x402_server(), x402_server_exports));
4394
- const result = await createX402Server2(config);
4522
+ const result = await createX402Server2(config, kvStore);
4395
4523
  await result.initPromise;
4396
4524
  return {
4397
4525
  server: result.server,
@@ -4574,7 +4702,7 @@ function createRouter(config) {
4574
4702
  mppSessionConfig: config.mpp?.session ? { depositMultiplier: config.mpp.session.depositMultiplier ?? 10 } : null
4575
4703
  };
4576
4704
  deps.initPromise = (async () => {
4577
- const x402Result = await initX402(config, x402ConfigError);
4705
+ const x402Result = await initX402(config, kvStore, x402ConfigError);
4578
4706
  deps.x402Server = x402Result.server ?? null;
4579
4707
  deps.x402FacilitatorsByNetwork = x402Result.facilitatorsByNetwork;
4580
4708
  if (x402Result.initError) deps.x402InitError = x402Result.initError;
@@ -4603,11 +4731,10 @@ function createRouter(config) {
4603
4731
  `[router] strictRoutes=true forbids key/path divergence for route '${definition.path}'. Remove custom \`key\` or make it equal to \`path\`.`
4604
4732
  );
4605
4733
  }
4606
- let builder = new RouteBuilder(key, registry, deps);
4734
+ let builder = new RouteBuilder(key, registry, deps, {
4735
+ protocols: config.protocols
4736
+ });
4607
4737
  builder = builder.path(normalizedPath);
4608
- if (config.protocols) {
4609
- builder._protocols = [...config.protocols];
4610
- }
4611
4738
  if (definition.method) {
4612
4739
  builder = builder.method(definition.method);
4613
4740
  }