@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.cjs +425 -298
- package/dist/index.d.cts +6 -30
- package/dist/index.d.ts +6 -30
- package/dist/index.js +425 -298
- package/package.json +1 -1
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(
|
|
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
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
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 (
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
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 =
|
|
3192
|
-
|
|
3193
|
-
|
|
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.
|
|
3198
|
-
if (this.
|
|
3319
|
+
const { pricing, resolvedOptions } = resolvePaidArgs(this.#s.key, pricingOrOptions, options);
|
|
3320
|
+
if (this.#s.authMode === "unprotected") {
|
|
3199
3321
|
throw new Error(
|
|
3200
|
-
`route '${this.
|
|
3322
|
+
`route '${this.#s.key}': Cannot combine .unprotected() and .paid() on the same route.`
|
|
3201
3323
|
);
|
|
3202
3324
|
}
|
|
3203
|
-
if (this.
|
|
3325
|
+
if (this.#s.pricing !== void 0) {
|
|
3204
3326
|
throw new Error(
|
|
3205
|
-
`route '${this.
|
|
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.
|
|
3210
|
-
next.
|
|
3331
|
+
next.#s.authMode = "paid";
|
|
3332
|
+
next.#s.pricing = pricing;
|
|
3211
3333
|
if (resolvedOptions?.protocols) {
|
|
3212
|
-
next.
|
|
3213
|
-
} else if (next.
|
|
3214
|
-
next.
|
|
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.
|
|
3217
|
-
if (resolvedOptions?.minPrice) next.
|
|
3218
|
-
if (resolvedOptions?.payTo) next.
|
|
3219
|
-
if (resolvedOptions?.mpp) next.
|
|
3220
|
-
if (resolvedOptions?.dynamic) next.
|
|
3221
|
-
if (resolvedOptions?.tickCost) next.
|
|
3222
|
-
if (resolvedOptions?.unitType) next.
|
|
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.
|
|
3346
|
+
if (next.#s.dynamicPrice) {
|
|
3225
3347
|
throw new Error(
|
|
3226
|
-
`route '${this.
|
|
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.
|
|
3353
|
+
throw new Error(`route '${this.#s.key}': tier key cannot be empty`);
|
|
3232
3354
|
}
|
|
3233
|
-
|
|
3234
|
-
if (isNaN(tierPrice) || tierPrice <= 0) {
|
|
3355
|
+
if (!isPositiveDecimal(tierConfig.price)) {
|
|
3235
3356
|
throw new Error(
|
|
3236
|
-
`route '${this.
|
|
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
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
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
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
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.
|
|
3258
|
-
throw new Error(`route '${this.
|
|
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.
|
|
3261
|
-
throw new Error(`route '${this.
|
|
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.
|
|
3391
|
+
if (this.#s.authMode === "unprotected") {
|
|
3277
3392
|
throw new Error(
|
|
3278
|
-
`route '${this.
|
|
3393
|
+
`route '${this.#s.key}': Cannot combine .unprotected() and .siwx() on the same route.`
|
|
3279
3394
|
);
|
|
3280
3395
|
}
|
|
3281
|
-
if (this.
|
|
3396
|
+
if (this.#s.apiKeyResolver) {
|
|
3282
3397
|
throw new Error(
|
|
3283
|
-
`route '${this.
|
|
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.
|
|
3288
|
-
if (next.
|
|
3289
|
-
next.
|
|
3290
|
-
if (next.
|
|
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.
|
|
3294
|
-
next.
|
|
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.
|
|
3426
|
+
if (this.#s.siwxEnabled) {
|
|
3312
3427
|
throw new Error(
|
|
3313
|
-
`route '${this.
|
|
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.
|
|
3318
|
-
next.
|
|
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.
|
|
3446
|
+
if (this.#s.authMode && this.#s.authMode !== "unprotected") {
|
|
3332
3447
|
throw new Error(
|
|
3333
|
-
`route '${this.
|
|
3448
|
+
`route '${this.#s.key}': Cannot combine .unprotected() and .${this.#s.authMode}() on the same route.`
|
|
3334
3449
|
);
|
|
3335
3450
|
}
|
|
3336
|
-
if (this.
|
|
3451
|
+
if (this.#s.pricing) {
|
|
3337
3452
|
throw new Error(
|
|
3338
|
-
`route '${this.
|
|
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.
|
|
3343
|
-
next.
|
|
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.
|
|
3363
|
-
next.
|
|
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.
|
|
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.
|
|
3395
|
-
next.
|
|
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.
|
|
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.
|
|
3427
|
-
next.
|
|
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.
|
|
3442
|
-
next.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
3679
|
+
if (!this.#s.authMode) {
|
|
3565
3680
|
throw new Error(
|
|
3566
|
-
`route '${this.
|
|
3681
|
+
`route '${this.#s.key}': Select an auth mode: .paid(pricing), .siwx(), .apiKey(resolver), or .unprotected()`
|
|
3567
3682
|
);
|
|
3568
3683
|
}
|
|
3569
|
-
if (this.
|
|
3684
|
+
if (this.#s.validateFn && !this.#s.bodySchema) {
|
|
3570
3685
|
throw new Error(
|
|
3571
|
-
`route '${this.
|
|
3686
|
+
`route '${this.#s.key}': .validate() requires .body() \u2014 validation runs on parsed body`
|
|
3572
3687
|
);
|
|
3573
3688
|
}
|
|
3574
|
-
if (this.
|
|
3575
|
-
throw new Error(`route '${this.
|
|
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.
|
|
3578
|
-
const hasUpto = this.
|
|
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.
|
|
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.
|
|
3586
|
-
if (!this.
|
|
3700
|
+
if (this.#s.dynamicPrice && this.#s.protocols.includes("mpp")) {
|
|
3701
|
+
if (!this.#s.deps.mppSessionConfig) {
|
|
3587
3702
|
throw new Error(
|
|
3588
|
-
`route '${this.
|
|
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.
|
|
3707
|
+
if (streaming && !this.#s.dynamicPrice) {
|
|
3593
3708
|
throw new Error(
|
|
3594
|
-
`route '${this.
|
|
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.
|
|
3599
|
-
this.
|
|
3600
|
-
this.
|
|
3601
|
-
this.
|
|
3602
|
-
this.
|
|
3603
|
-
this.
|
|
3604
|
-
this.
|
|
3605
|
-
this.
|
|
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.
|
|
3609
|
-
authMode: this.
|
|
3610
|
-
siwxEnabled: this.
|
|
3611
|
-
pricing: this.
|
|
3612
|
-
dynamicPrice: this.
|
|
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.
|
|
3615
|
-
bodySchema: this.
|
|
3616
|
-
querySchema: this.
|
|
3617
|
-
outputSchema: this.
|
|
3618
|
-
inputExample: this.
|
|
3619
|
-
outputExample: this.
|
|
3620
|
-
description: this.
|
|
3621
|
-
path: this.
|
|
3622
|
-
method: this.
|
|
3623
|
-
maxPrice: this.
|
|
3624
|
-
minPrice: this.
|
|
3625
|
-
payTo: this.
|
|
3626
|
-
apiKeyResolver: this.
|
|
3627
|
-
providerName: this.
|
|
3628
|
-
providerConfig: this.
|
|
3629
|
-
validateFn: this.
|
|
3630
|
-
settlement: this.
|
|
3631
|
-
mppInfo: this.
|
|
3632
|
-
tickCost: this.
|
|
3633
|
-
unitType: this.
|
|
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.
|
|
3636
|
-
return createRequestHandler(entry, handlerFn, this.
|
|
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) =>
|
|
3881
|
-
const
|
|
3882
|
-
|
|
3883
|
-
|
|
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:
|
|
4000
|
+
price: { mode: "fixed", currency: "USD", amount: extrema.min }
|
|
3887
4001
|
};
|
|
3888
4002
|
}
|
|
3889
4003
|
return {
|
|
3890
|
-
price: { mode: "dynamic", currency: "USD", min:
|
|
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
|
}
|