@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.cjs
CHANGED
|
@@ -264,12 +264,139 @@ var init_facilitators = __esm({
|
|
|
264
264
|
}
|
|
265
265
|
});
|
|
266
266
|
|
|
267
|
+
// src/kv-store/facilitator-supported.ts
|
|
268
|
+
function withCachedSupported(inner, options = {}) {
|
|
269
|
+
const { kv, cacheKey, ttlSeconds = FACILITATOR_SUPPORTED_TTL_SECONDS, fallback } = options;
|
|
270
|
+
const kvKey = kv && cacheKey ? `${FACILITATOR_SUPPORTED_KV_PREFIX}${cacheKey}` : void 0;
|
|
271
|
+
let inflight;
|
|
272
|
+
return {
|
|
273
|
+
verify: inner.verify.bind(inner),
|
|
274
|
+
settle: inner.settle.bind(inner),
|
|
275
|
+
getSupported: () => {
|
|
276
|
+
if (inflight) return inflight;
|
|
277
|
+
const attempt = fetchSupported(inner, kv, kvKey, ttlSeconds, fallback);
|
|
278
|
+
inflight = attempt;
|
|
279
|
+
attempt.catch(() => {
|
|
280
|
+
if (inflight === attempt) inflight = void 0;
|
|
281
|
+
});
|
|
282
|
+
return attempt;
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
async function fetchSupported(inner, kv, kvKey, ttlSeconds, fallback) {
|
|
287
|
+
if (kv && kvKey) {
|
|
288
|
+
const cached = await readKvCache(kv, kvKey);
|
|
289
|
+
if (cached) return cached;
|
|
290
|
+
}
|
|
291
|
+
const fresh = await tryFetchLive(inner, fallback);
|
|
292
|
+
if (fresh === null) return fallback();
|
|
293
|
+
if (kv && kvKey) await writeKvCache(kv, kvKey, fresh, ttlSeconds);
|
|
294
|
+
return fresh;
|
|
295
|
+
}
|
|
296
|
+
async function tryFetchLive(inner, fallback) {
|
|
297
|
+
try {
|
|
298
|
+
return await inner.getSupported();
|
|
299
|
+
} catch (err) {
|
|
300
|
+
if (!fallback) throw err;
|
|
301
|
+
console.warn(
|
|
302
|
+
`[x402] facilitator /supported failed, using hardcoded baseline: ${err instanceof Error ? err.message : String(err)}`
|
|
303
|
+
);
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
async function readKvCache(kv, key) {
|
|
308
|
+
try {
|
|
309
|
+
const cached = await kv.get(key);
|
|
310
|
+
return isSupportedResponse(cached) ? cached : void 0;
|
|
311
|
+
} catch {
|
|
312
|
+
return void 0;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
async function writeKvCache(kv, key, value, ttlSeconds) {
|
|
316
|
+
try {
|
|
317
|
+
await kv.setNxEx(key, value, ttlSeconds);
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function isSupportedResponse(value) {
|
|
322
|
+
return typeof value === "object" && value !== null && Array.isArray(value.kinds);
|
|
323
|
+
}
|
|
324
|
+
var FACILITATOR_SUPPORTED_TTL_SECONDS, FACILITATOR_SUPPORTED_KV_PREFIX;
|
|
325
|
+
var init_facilitator_supported = __esm({
|
|
326
|
+
"src/kv-store/facilitator-supported.ts"() {
|
|
327
|
+
"use strict";
|
|
328
|
+
FACILITATOR_SUPPORTED_TTL_SECONDS = 60 * 60;
|
|
329
|
+
FACILITATOR_SUPPORTED_KV_PREFIX = "x402:facilitator-supported:";
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// src/protocols/x402/facilitator-clients.ts
|
|
334
|
+
function createFacilitatorClients(facilitatorsByNetwork, HTTPFacilitatorClient, kvStore) {
|
|
335
|
+
return getResolvedX402FacilitatorGroups(facilitatorsByNetwork).map((group) => {
|
|
336
|
+
const inner = new HTTPFacilitatorClient(group.config);
|
|
337
|
+
const kinds = buildSupportedKinds(group);
|
|
338
|
+
const baseline = () => ({
|
|
339
|
+
kinds,
|
|
340
|
+
extensions: [],
|
|
341
|
+
signers: {}
|
|
342
|
+
});
|
|
343
|
+
if (group.family === "solana") {
|
|
344
|
+
return hardcodedSupportedClient(inner, baseline);
|
|
345
|
+
}
|
|
346
|
+
const cached = withCachedSupported(inner, {
|
|
347
|
+
kv: kvStore,
|
|
348
|
+
cacheKey: group.config.url,
|
|
349
|
+
fallback: baseline
|
|
350
|
+
});
|
|
351
|
+
return withScopedKinds(cached, kinds);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
function hardcodedSupportedClient(inner, build) {
|
|
355
|
+
return {
|
|
356
|
+
verify: inner.verify.bind(inner),
|
|
357
|
+
settle: inner.settle.bind(inner),
|
|
358
|
+
getSupported: async () => build()
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
function withScopedKinds(client, kinds) {
|
|
362
|
+
return {
|
|
363
|
+
verify: client.verify.bind(client),
|
|
364
|
+
settle: client.settle.bind(client),
|
|
365
|
+
getSupported: async () => ({ ...await client.getSupported(), kinds })
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
function buildSupportedKinds(group) {
|
|
369
|
+
return group.networks.flatMap((network) => {
|
|
370
|
+
if (group.family === "solana") {
|
|
371
|
+
return [
|
|
372
|
+
{
|
|
373
|
+
x402Version: 2,
|
|
374
|
+
scheme: "exact",
|
|
375
|
+
network,
|
|
376
|
+
extra: { features: { xSettlementAccountSupported: true } }
|
|
377
|
+
}
|
|
378
|
+
];
|
|
379
|
+
}
|
|
380
|
+
return [
|
|
381
|
+
{ x402Version: 2, scheme: "exact", network },
|
|
382
|
+
{ x402Version: 2, scheme: "upto", network }
|
|
383
|
+
];
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
var init_facilitator_clients = __esm({
|
|
387
|
+
"src/protocols/x402/facilitator-clients.ts"() {
|
|
388
|
+
"use strict";
|
|
389
|
+
init_facilitator_supported();
|
|
390
|
+
init_facilitators();
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
|
|
267
394
|
// src/init/x402-server.ts
|
|
268
395
|
var x402_server_exports = {};
|
|
269
396
|
__export(x402_server_exports, {
|
|
270
397
|
createX402Server: () => createX402Server
|
|
271
398
|
});
|
|
272
|
-
async function createX402Server(config) {
|
|
399
|
+
async function createX402Server(config, kvStore) {
|
|
273
400
|
const { x402ResourceServer, HTTPFacilitatorClient } = await import("@x402/core/server");
|
|
274
401
|
const { registerExactEvmScheme } = await import("@x402/evm/exact/server");
|
|
275
402
|
const { bazaarResourceServerExtension } = await import("@x402/extensions/bazaar");
|
|
@@ -283,7 +410,11 @@ async function createX402Server(config) {
|
|
|
283
410
|
);
|
|
284
411
|
const evmNetworks = filterEvmNetworks(configuredNetworks);
|
|
285
412
|
const svmNetworks = filterSolanaNetworks(configuredNetworks);
|
|
286
|
-
const facilitatorClients = createFacilitatorClients(
|
|
413
|
+
const facilitatorClients = createFacilitatorClients(
|
|
414
|
+
facilitatorsByNetwork,
|
|
415
|
+
HTTPFacilitatorClient,
|
|
416
|
+
kvStore
|
|
417
|
+
);
|
|
287
418
|
const server = new x402ResourceServer(
|
|
288
419
|
facilitatorClients.length === 1 ? facilitatorClients[0] : facilitatorClients
|
|
289
420
|
);
|
|
@@ -307,48 +438,13 @@ async function createX402Server(config) {
|
|
|
307
438
|
facilitatorsByNetwork
|
|
308
439
|
};
|
|
309
440
|
}
|
|
310
|
-
function createFacilitatorClients(facilitatorsByNetwork, HTTPFacilitatorClient) {
|
|
311
|
-
const groups = getResolvedX402FacilitatorGroups(facilitatorsByNetwork);
|
|
312
|
-
return groups.map((group) => {
|
|
313
|
-
const inner = new HTTPFacilitatorClient(group.config);
|
|
314
|
-
const kinds = buildSupportedKinds(group);
|
|
315
|
-
return hardcodedSupportedClient(inner, kinds);
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
function hardcodedSupportedClient(inner, kinds) {
|
|
319
|
-
return {
|
|
320
|
-
verify: inner.verify.bind(inner),
|
|
321
|
-
settle: inner.settle.bind(inner),
|
|
322
|
-
getSupported: async () => ({ kinds, extensions: [], signers: {} })
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
function buildSupportedKinds(group) {
|
|
326
|
-
return group.networks.flatMap((network) => {
|
|
327
|
-
const exactKind = {
|
|
328
|
-
x402Version: 2,
|
|
329
|
-
scheme: "exact",
|
|
330
|
-
network,
|
|
331
|
-
...group.family === "solana" ? {
|
|
332
|
-
extra: {
|
|
333
|
-
features: {
|
|
334
|
-
xSettlementAccountSupported: true
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
} : {}
|
|
338
|
-
};
|
|
339
|
-
const uptoKind = { x402Version: 2, scheme: "upto", network };
|
|
340
|
-
if (group.family === "evm") {
|
|
341
|
-
return [exactKind, uptoKind];
|
|
342
|
-
}
|
|
343
|
-
return [exactKind, uptoKind];
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
441
|
var init_x402_server = __esm({
|
|
347
442
|
"src/init/x402-server.ts"() {
|
|
348
443
|
"use strict";
|
|
349
444
|
init_evm();
|
|
350
445
|
init_solana();
|
|
351
446
|
init_facilitators();
|
|
447
|
+
init_facilitator_clients();
|
|
352
448
|
init_accepts();
|
|
353
449
|
}
|
|
354
450
|
});
|
|
@@ -1069,6 +1165,61 @@ async function runApiKeyOnlyFlow(ctx) {
|
|
|
1069
1165
|
return runHandlerOnly(ctx, null, result.account);
|
|
1070
1166
|
}
|
|
1071
1167
|
|
|
1168
|
+
// src/pricing/format.ts
|
|
1169
|
+
var USDC_DECIMALS = 6;
|
|
1170
|
+
var DECIMAL_RE = /^(\d+)(?:\.(\d+))?$/;
|
|
1171
|
+
function badDecimal(amount) {
|
|
1172
|
+
return Object.assign(new Error(`'${amount}' is not a valid decimal-dollar string`), {
|
|
1173
|
+
status: 400
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
function decimalToAtomic(amount, decimals = USDC_DECIMALS) {
|
|
1177
|
+
const match = DECIMAL_RE.exec(amount.trim());
|
|
1178
|
+
if (!match) throw badDecimal(amount);
|
|
1179
|
+
const whole = match[1];
|
|
1180
|
+
const fraction = match[2] ?? "";
|
|
1181
|
+
if (fraction.length > decimals) {
|
|
1182
|
+
throw Object.assign(new Error(`Amount '${amount}' exceeds ${decimals} decimal places`), {
|
|
1183
|
+
status: 400
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
const normalized = `${whole}${fraction.padEnd(decimals, "0")}`.replace(/^0+(?=\d)/, "");
|
|
1187
|
+
return BigInt(normalized || "0");
|
|
1188
|
+
}
|
|
1189
|
+
function atomicToDecimal(atomic, decimals = USDC_DECIMALS) {
|
|
1190
|
+
const divisor = 10n ** BigInt(decimals);
|
|
1191
|
+
const whole = atomic / divisor;
|
|
1192
|
+
const fraction = atomic % divisor;
|
|
1193
|
+
if (fraction === 0n) return whole.toString();
|
|
1194
|
+
const fractionStr = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
1195
|
+
return `${whole}.${fractionStr}`;
|
|
1196
|
+
}
|
|
1197
|
+
function compareDecimals(a, b) {
|
|
1198
|
+
const av = decimalToAtomic(a);
|
|
1199
|
+
const bv = decimalToAtomic(b);
|
|
1200
|
+
if (av < bv) return -1;
|
|
1201
|
+
if (av > bv) return 1;
|
|
1202
|
+
return 0;
|
|
1203
|
+
}
|
|
1204
|
+
function isPositiveDecimal(value) {
|
|
1205
|
+
try {
|
|
1206
|
+
return decimalToAtomic(value) > 0n;
|
|
1207
|
+
} catch {
|
|
1208
|
+
return false;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
function multiplyDecimal(decimal, factor) {
|
|
1212
|
+
if (!Number.isFinite(factor) || factor <= 0) return decimal;
|
|
1213
|
+
const [whole, fraction = ""] = decimal.split(".");
|
|
1214
|
+
const scaled = (BigInt(whole + fraction) * BigInt(factor)).toString();
|
|
1215
|
+
const decimals = fraction.length;
|
|
1216
|
+
if (decimals === 0) return scaled;
|
|
1217
|
+
const padded = scaled.padStart(decimals + 1, "0");
|
|
1218
|
+
const intPart = padded.slice(0, padded.length - decimals);
|
|
1219
|
+
const fracPart = padded.slice(padded.length - decimals).replace(/0+$/, "");
|
|
1220
|
+
return fracPart ? `${intPart}.${fracPart}` : intPart;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1072
1223
|
// src/pricing/dynamic.ts
|
|
1073
1224
|
var DynamicPricing = class {
|
|
1074
1225
|
constructor(opts) {
|
|
@@ -1104,9 +1255,13 @@ var DynamicPricing = class {
|
|
|
1104
1255
|
}
|
|
1105
1256
|
cap(raw, body) {
|
|
1106
1257
|
if (!this.opts.maxPrice) return raw;
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1258
|
+
let overCap;
|
|
1259
|
+
try {
|
|
1260
|
+
overCap = compareDecimals(raw, this.opts.maxPrice) > 0;
|
|
1261
|
+
} catch {
|
|
1262
|
+
overCap = true;
|
|
1263
|
+
}
|
|
1264
|
+
if (overCap) {
|
|
1110
1265
|
this.alert("warn", `Price ${raw} exceeds maxPrice ${this.opts.maxPrice}, capping`, {
|
|
1111
1266
|
calculated: raw,
|
|
1112
1267
|
maxPrice: this.opts.maxPrice,
|
|
@@ -1183,7 +1338,7 @@ var TieredPricing = class {
|
|
|
1183
1338
|
maxTierPrice() {
|
|
1184
1339
|
let max = "0";
|
|
1185
1340
|
for (const tier of Object.values(this.opts.tiers)) {
|
|
1186
|
-
if (
|
|
1341
|
+
if (compareDecimals(tier.price, max) > 0) max = tier.price;
|
|
1187
1342
|
}
|
|
1188
1343
|
return max;
|
|
1189
1344
|
}
|
|
@@ -1661,17 +1816,6 @@ var mppStrategy = {
|
|
|
1661
1816
|
return buildChargeChallenge(args);
|
|
1662
1817
|
}
|
|
1663
1818
|
};
|
|
1664
|
-
function multiplyDecimal(decimal, factor) {
|
|
1665
|
-
if (!Number.isFinite(factor) || factor <= 0) return decimal;
|
|
1666
|
-
const [whole, fraction = ""] = decimal.split(".");
|
|
1667
|
-
const scaled = (BigInt(whole + fraction) * BigInt(factor)).toString();
|
|
1668
|
-
const decimals = fraction.length;
|
|
1669
|
-
if (decimals === 0) return scaled;
|
|
1670
|
-
const padded = scaled.padStart(decimals + 1, "0");
|
|
1671
|
-
const intPart = padded.slice(0, padded.length - decimals);
|
|
1672
|
-
const fracPart = padded.slice(padded.length - decimals).replace(/0+$/, "");
|
|
1673
|
-
return fracPart ? `${intPart}.${fracPart}` : intPart;
|
|
1674
|
-
}
|
|
1675
1819
|
async function buildChargeChallenge(args) {
|
|
1676
1820
|
if (!args.deps.mppx) return {};
|
|
1677
1821
|
try {
|
|
@@ -1764,26 +1908,13 @@ function buildCustomRequirement(price, accept) {
|
|
|
1764
1908
|
return {
|
|
1765
1909
|
scheme: accept.scheme,
|
|
1766
1910
|
network: accept.network,
|
|
1767
|
-
amount:
|
|
1911
|
+
amount: decimalToAtomic(price, accept.decimals ?? 6).toString(),
|
|
1768
1912
|
asset: accept.asset,
|
|
1769
1913
|
payTo: accept.payTo,
|
|
1770
1914
|
maxTimeoutSeconds: accept.maxTimeoutSeconds ?? 300,
|
|
1771
1915
|
extra: accept.extra ?? {}
|
|
1772
1916
|
};
|
|
1773
1917
|
}
|
|
1774
|
-
function decimalToAtomicUnits(amount, decimals) {
|
|
1775
|
-
const match = /^(?<whole>\d+)(?:\.(?<fraction>\d+))?$/.exec(amount);
|
|
1776
|
-
if (!match?.groups) {
|
|
1777
|
-
throw new Error(`Invalid decimal amount '${amount}'`);
|
|
1778
|
-
}
|
|
1779
|
-
const whole = match.groups.whole;
|
|
1780
|
-
const fraction = match.groups.fraction ?? "";
|
|
1781
|
-
if (fraction.length > decimals) {
|
|
1782
|
-
throw new Error(`Amount '${amount}' exceeds ${decimals} decimal places`);
|
|
1783
|
-
}
|
|
1784
|
-
const normalized = `${whole}${fraction.padEnd(decimals, "0")}`.replace(/^0+(?=\d)/, "");
|
|
1785
|
-
return normalized === "" ? "0" : normalized;
|
|
1786
|
-
}
|
|
1787
1918
|
|
|
1788
1919
|
// src/protocols/x402/challenge.ts
|
|
1789
1920
|
async function buildX402Challenge(opts) {
|
|
@@ -2146,13 +2277,14 @@ async function buildX402ChallengeContribution(args) {
|
|
|
2146
2277
|
}
|
|
2147
2278
|
function reportSettleFailure(report, err, network) {
|
|
2148
2279
|
const facilitator = err ?? {};
|
|
2149
|
-
|
|
2280
|
+
const meta = {
|
|
2150
2281
|
error: err instanceof Error ? err.message : String(err),
|
|
2151
2282
|
network,
|
|
2152
2283
|
errorReason: facilitator.errorReason,
|
|
2153
2284
|
facilitatorStatus: facilitator.response?.status,
|
|
2154
2285
|
facilitatorBody: facilitator.response?.data ?? facilitator.response?.body
|
|
2155
|
-
}
|
|
2286
|
+
};
|
|
2287
|
+
report("error", "Settlement failed", meta);
|
|
2156
2288
|
}
|
|
2157
2289
|
|
|
2158
2290
|
// src/protocols/index.ts
|
|
@@ -2175,6 +2307,7 @@ function getAllowedStrategies(allowed) {
|
|
|
2175
2307
|
var import_server4 = require("next/server");
|
|
2176
2308
|
|
|
2177
2309
|
// src/pipeline/challenge-extensions.ts
|
|
2310
|
+
init_evm();
|
|
2178
2311
|
async function buildChallengeExtensions(ctx) {
|
|
2179
2312
|
const { routeEntry } = ctx;
|
|
2180
2313
|
let extensions;
|
|
@@ -2219,6 +2352,23 @@ async function buildChallengeExtensions(ctx) {
|
|
|
2219
2352
|
} catch {
|
|
2220
2353
|
}
|
|
2221
2354
|
}
|
|
2355
|
+
const hasEvmUpto = ctx.deps.x402Accepts.some(
|
|
2356
|
+
(accept) => accept.scheme === "upto" && isEvmNetwork(accept.network)
|
|
2357
|
+
);
|
|
2358
|
+
if (hasEvmUpto) {
|
|
2359
|
+
try {
|
|
2360
|
+
const { declareEip2612GasSponsoringExtension } = await import("@x402/extensions");
|
|
2361
|
+
extensions = {
|
|
2362
|
+
...extensions ?? {},
|
|
2363
|
+
...declareEip2612GasSponsoringExtension()
|
|
2364
|
+
};
|
|
2365
|
+
} catch (err) {
|
|
2366
|
+
ctx.report(
|
|
2367
|
+
"warn",
|
|
2368
|
+
`EIP-2612 gas-sponsoring declaration failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2369
|
+
);
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2222
2372
|
return extensions;
|
|
2223
2373
|
}
|
|
2224
2374
|
|
|
@@ -2374,27 +2524,6 @@ async function runDynamicChannelMgmtFlow(args) {
|
|
|
2374
2524
|
// src/pipeline/flows/dynamic/dynamic-invoke.ts
|
|
2375
2525
|
var import_server6 = require("next/server");
|
|
2376
2526
|
|
|
2377
|
-
// src/pricing/atomic.ts
|
|
2378
|
-
var USDC_DECIMALS = 6;
|
|
2379
|
-
function decimalToAtomic(amount) {
|
|
2380
|
-
const m = /^(\d+)(?:\.(\d+))?$/.exec(amount.trim());
|
|
2381
|
-
if (!m) {
|
|
2382
|
-
throw Object.assign(new Error(`'${amount}' is not a valid decimal-dollar string`), {
|
|
2383
|
-
status: 400
|
|
2384
|
-
});
|
|
2385
|
-
}
|
|
2386
|
-
const whole = m[1];
|
|
2387
|
-
const fraction = (m[2] ?? "").slice(0, USDC_DECIMALS).padEnd(USDC_DECIMALS, "0");
|
|
2388
|
-
return BigInt(`${whole}${fraction}`.replace(/^0+(?=\d)/, "") || "0");
|
|
2389
|
-
}
|
|
2390
|
-
function atomicToDecimal(atomic) {
|
|
2391
|
-
const whole = atomic / 10n ** BigInt(USDC_DECIMALS);
|
|
2392
|
-
const fraction = atomic % 10n ** BigInt(USDC_DECIMALS);
|
|
2393
|
-
if (fraction === 0n) return whole.toString();
|
|
2394
|
-
const fractionStr = fraction.toString().padStart(USDC_DECIMALS, "0").replace(/0+$/, "");
|
|
2395
|
-
return `${whole}.${fractionStr}`;
|
|
2396
|
-
}
|
|
2397
|
-
|
|
2398
2527
|
// src/pricing/charge-context.ts
|
|
2399
2528
|
function createChargeContext(args) {
|
|
2400
2529
|
const { tickCost, maxPrice, route } = args;
|
|
@@ -2805,6 +2934,19 @@ async function runPaidFlow(ctx) {
|
|
|
2805
2934
|
var import_server7 = require("next/server");
|
|
2806
2935
|
|
|
2807
2936
|
// src/kv-store/client.ts
|
|
2937
|
+
var BIGINT_SUFFIX = "#__bigint";
|
|
2938
|
+
function stringifyValue(value) {
|
|
2939
|
+
return JSON.stringify(
|
|
2940
|
+
value,
|
|
2941
|
+
(_key, v) => typeof v === "bigint" ? `${v.toString()}${BIGINT_SUFFIX}` : v
|
|
2942
|
+
);
|
|
2943
|
+
}
|
|
2944
|
+
function parseValue(raw) {
|
|
2945
|
+
return JSON.parse(
|
|
2946
|
+
raw,
|
|
2947
|
+
(_key, v) => typeof v === "string" && v.endsWith(BIGINT_SUFFIX) ? BigInt(v.slice(0, -BIGINT_SUFFIX.length)) : v
|
|
2948
|
+
);
|
|
2949
|
+
}
|
|
2808
2950
|
function restKvStore(url, token) {
|
|
2809
2951
|
const base = url.replace(/\/+$/, "");
|
|
2810
2952
|
const authHeader = { Authorization: `Bearer ${token}` };
|
|
@@ -2826,16 +2968,22 @@ function restKvStore(url, token) {
|
|
|
2826
2968
|
const res = await fetch(`${base}/get/${encodeURIComponent(key)}`, { headers: authHeader });
|
|
2827
2969
|
if (!res.ok) throw new Error(`[kv-store] GET ${key}: ${res.status}`);
|
|
2828
2970
|
const { result } = await res.json();
|
|
2829
|
-
|
|
2971
|
+
if (result == null) return null;
|
|
2972
|
+
if (typeof result !== "string") return result;
|
|
2973
|
+
try {
|
|
2974
|
+
return parseValue(result);
|
|
2975
|
+
} catch {
|
|
2976
|
+
return result;
|
|
2977
|
+
}
|
|
2830
2978
|
}
|
|
2831
2979
|
async function set(key, value) {
|
|
2832
|
-
await exec(["SET", key,
|
|
2980
|
+
await exec(["SET", key, stringifyValue(value)]);
|
|
2833
2981
|
}
|
|
2834
2982
|
async function del(key) {
|
|
2835
2983
|
await exec(["DEL", key]);
|
|
2836
2984
|
}
|
|
2837
2985
|
async function setNxEx(key, value, ttlSeconds) {
|
|
2838
|
-
const result = await exec(["SET", key,
|
|
2986
|
+
const result = await exec(["SET", key, stringifyValue(value), "EX", ttlSeconds, "NX"]);
|
|
2839
2987
|
return result === "OK";
|
|
2840
2988
|
}
|
|
2841
2989
|
async function sadd(key, member) {
|
|
@@ -3164,142 +3312,109 @@ ${issues}`
|
|
|
3164
3312
|
}
|
|
3165
3313
|
|
|
3166
3314
|
// src/builder.ts
|
|
3167
|
-
var RouteBuilder = class {
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
_inputExample = void 0;
|
|
3202
|
-
/** @internal */
|
|
3203
|
-
_hasInputExample = false;
|
|
3204
|
-
/** @internal */
|
|
3205
|
-
_outputExample = void 0;
|
|
3206
|
-
/** @internal */
|
|
3207
|
-
_hasOutputExample = false;
|
|
3208
|
-
/** @internal */
|
|
3209
|
-
_description;
|
|
3210
|
-
/** @internal */
|
|
3211
|
-
_path;
|
|
3212
|
-
/** @internal */
|
|
3213
|
-
_method = "POST";
|
|
3214
|
-
/** @internal */
|
|
3215
|
-
_apiKeyResolver;
|
|
3216
|
-
/** @internal */
|
|
3217
|
-
_providerName;
|
|
3218
|
-
/** @internal */
|
|
3219
|
-
_providerConfig;
|
|
3220
|
-
/** @internal */
|
|
3221
|
-
_validateFn;
|
|
3222
|
-
/** @internal */
|
|
3223
|
-
_settlement;
|
|
3224
|
-
/** @internal */
|
|
3225
|
-
_mppInfo;
|
|
3226
|
-
constructor(key, registry, deps) {
|
|
3227
|
-
this._key = key;
|
|
3228
|
-
this._registry = registry;
|
|
3229
|
-
this._deps = deps;
|
|
3315
|
+
var RouteBuilder = class _RouteBuilder {
|
|
3316
|
+
#s;
|
|
3317
|
+
constructor(key, registry, deps, defaults) {
|
|
3318
|
+
this.#s = {
|
|
3319
|
+
key,
|
|
3320
|
+
registry,
|
|
3321
|
+
deps,
|
|
3322
|
+
authMode: null,
|
|
3323
|
+
pricing: void 0,
|
|
3324
|
+
siwxEnabled: false,
|
|
3325
|
+
protocols: defaults?.protocols ? [...defaults.protocols] : ["x402"],
|
|
3326
|
+
maxPrice: void 0,
|
|
3327
|
+
minPrice: void 0,
|
|
3328
|
+
dynamicPrice: false,
|
|
3329
|
+
tickCost: void 0,
|
|
3330
|
+
unitType: void 0,
|
|
3331
|
+
payTo: void 0,
|
|
3332
|
+
bodySchema: void 0,
|
|
3333
|
+
querySchema: void 0,
|
|
3334
|
+
outputSchema: void 0,
|
|
3335
|
+
inputExample: void 0,
|
|
3336
|
+
hasInputExample: false,
|
|
3337
|
+
outputExample: void 0,
|
|
3338
|
+
hasOutputExample: false,
|
|
3339
|
+
description: void 0,
|
|
3340
|
+
path: void 0,
|
|
3341
|
+
method: "POST",
|
|
3342
|
+
apiKeyResolver: void 0,
|
|
3343
|
+
providerName: void 0,
|
|
3344
|
+
providerConfig: void 0,
|
|
3345
|
+
validateFn: void 0,
|
|
3346
|
+
settlement: void 0,
|
|
3347
|
+
mppInfo: void 0
|
|
3348
|
+
};
|
|
3230
3349
|
}
|
|
3231
3350
|
fork() {
|
|
3232
|
-
const next =
|
|
3233
|
-
|
|
3234
|
-
|
|
3351
|
+
const next = new _RouteBuilder(
|
|
3352
|
+
this.#s.key,
|
|
3353
|
+
this.#s.registry,
|
|
3354
|
+
this.#s.deps
|
|
3355
|
+
);
|
|
3356
|
+
next.#s = { ...this.#s, protocols: [...this.#s.protocols] };
|
|
3235
3357
|
return next;
|
|
3236
3358
|
}
|
|
3237
3359
|
paid(pricingOrOptions, options) {
|
|
3238
|
-
const { pricing, resolvedOptions } = resolvePaidArgs(this.
|
|
3239
|
-
if (this.
|
|
3360
|
+
const { pricing, resolvedOptions } = resolvePaidArgs(this.#s.key, pricingOrOptions, options);
|
|
3361
|
+
if (this.#s.authMode === "unprotected") {
|
|
3240
3362
|
throw new Error(
|
|
3241
|
-
`route '${this.
|
|
3363
|
+
`route '${this.#s.key}': Cannot combine .unprotected() and .paid() on the same route.`
|
|
3242
3364
|
);
|
|
3243
3365
|
}
|
|
3244
|
-
if (this.
|
|
3366
|
+
if (this.#s.pricing !== void 0) {
|
|
3245
3367
|
throw new Error(
|
|
3246
|
-
`route '${this.
|
|
3368
|
+
`route '${this.#s.key}': Cannot call .paid() more than once on the same route.`
|
|
3247
3369
|
);
|
|
3248
3370
|
}
|
|
3249
3371
|
const next = this.fork();
|
|
3250
|
-
next.
|
|
3251
|
-
next.
|
|
3372
|
+
next.#s.authMode = "paid";
|
|
3373
|
+
next.#s.pricing = pricing;
|
|
3252
3374
|
if (resolvedOptions?.protocols) {
|
|
3253
|
-
next.
|
|
3254
|
-
} else if (next.
|
|
3255
|
-
next.
|
|
3375
|
+
next.#s.protocols = [...resolvedOptions.protocols];
|
|
3376
|
+
} else if (next.#s.protocols.length === 0) {
|
|
3377
|
+
next.#s.protocols = ["x402"];
|
|
3256
3378
|
}
|
|
3257
|
-
if (resolvedOptions?.maxPrice) next.
|
|
3258
|
-
if (resolvedOptions?.minPrice) next.
|
|
3259
|
-
if (resolvedOptions?.payTo) next.
|
|
3260
|
-
if (resolvedOptions?.mpp) next.
|
|
3261
|
-
if (resolvedOptions?.dynamic) next.
|
|
3262
|
-
if (resolvedOptions?.tickCost) next.
|
|
3263
|
-
if (resolvedOptions?.unitType) next.
|
|
3379
|
+
if (resolvedOptions?.maxPrice) next.#s.maxPrice = resolvedOptions.maxPrice;
|
|
3380
|
+
if (resolvedOptions?.minPrice) next.#s.minPrice = resolvedOptions.minPrice;
|
|
3381
|
+
if (resolvedOptions?.payTo) next.#s.payTo = resolvedOptions.payTo;
|
|
3382
|
+
if (resolvedOptions?.mpp) next.#s.mppInfo = resolvedOptions.mpp;
|
|
3383
|
+
if (resolvedOptions?.dynamic) next.#s.dynamicPrice = true;
|
|
3384
|
+
if (resolvedOptions?.tickCost) next.#s.tickCost = resolvedOptions.tickCost;
|
|
3385
|
+
if (resolvedOptions?.unitType) next.#s.unitType = resolvedOptions.unitType;
|
|
3264
3386
|
if (typeof pricing === "object" && "tiers" in pricing) {
|
|
3265
|
-
if (next.
|
|
3387
|
+
if (next.#s.dynamicPrice) {
|
|
3266
3388
|
throw new Error(
|
|
3267
|
-
`route '${this.
|
|
3389
|
+
`route '${this.#s.key}': .paid({ dynamic: true }) is incompatible with tiered pricing`
|
|
3268
3390
|
);
|
|
3269
3391
|
}
|
|
3270
3392
|
for (const [tierKey, tierConfig] of Object.entries(pricing.tiers)) {
|
|
3271
3393
|
if (!tierKey) {
|
|
3272
|
-
throw new Error(`route '${this.
|
|
3394
|
+
throw new Error(`route '${this.#s.key}': tier key cannot be empty`);
|
|
3273
3395
|
}
|
|
3274
|
-
|
|
3275
|
-
if (isNaN(tierPrice) || tierPrice <= 0) {
|
|
3396
|
+
if (!isPositiveDecimal(tierConfig.price)) {
|
|
3276
3397
|
throw new Error(
|
|
3277
|
-
`route '${this.
|
|
3398
|
+
`route '${this.#s.key}': tier '${tierKey}' price '${tierConfig.price}' must be a positive decimal string`
|
|
3278
3399
|
);
|
|
3279
3400
|
}
|
|
3280
3401
|
}
|
|
3281
3402
|
}
|
|
3282
|
-
if (resolvedOptions?.maxPrice !== void 0) {
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
`route '${this._key}': maxPrice '${resolvedOptions.maxPrice}' must be a positive decimal string`
|
|
3287
|
-
);
|
|
3288
|
-
}
|
|
3403
|
+
if (resolvedOptions?.maxPrice !== void 0 && !isPositiveDecimal(resolvedOptions.maxPrice)) {
|
|
3404
|
+
throw new Error(
|
|
3405
|
+
`route '${this.#s.key}': maxPrice '${resolvedOptions.maxPrice}' must be a positive decimal string`
|
|
3406
|
+
);
|
|
3289
3407
|
}
|
|
3290
|
-
if (resolvedOptions?.tickCost !== void 0) {
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
`route '${this._key}': tickCost '${resolvedOptions.tickCost}' must be a positive decimal string`
|
|
3295
|
-
);
|
|
3296
|
-
}
|
|
3408
|
+
if (resolvedOptions?.tickCost !== void 0 && !isPositiveDecimal(resolvedOptions.tickCost)) {
|
|
3409
|
+
throw new Error(
|
|
3410
|
+
`route '${this.#s.key}': tickCost '${resolvedOptions.tickCost}' must be a positive decimal string`
|
|
3411
|
+
);
|
|
3297
3412
|
}
|
|
3298
|
-
if (next.
|
|
3299
|
-
throw new Error(`route '${this.
|
|
3413
|
+
if (next.#s.dynamicPrice && !next.#s.maxPrice) {
|
|
3414
|
+
throw new Error(`route '${this.#s.key}': .paid({ dynamic: true }) requires maxPrice`);
|
|
3300
3415
|
}
|
|
3301
|
-
if (next.
|
|
3302
|
-
throw new Error(`route '${this.
|
|
3416
|
+
if (next.#s.dynamicPrice && !next.#s.tickCost) {
|
|
3417
|
+
throw new Error(`route '${this.#s.key}': .paid({ dynamic: true }) requires tickCost`);
|
|
3303
3418
|
}
|
|
3304
3419
|
return next;
|
|
3305
3420
|
}
|
|
@@ -3314,25 +3429,25 @@ var RouteBuilder = class {
|
|
|
3314
3429
|
* ```
|
|
3315
3430
|
*/
|
|
3316
3431
|
siwx() {
|
|
3317
|
-
if (this.
|
|
3432
|
+
if (this.#s.authMode === "unprotected") {
|
|
3318
3433
|
throw new Error(
|
|
3319
|
-
`route '${this.
|
|
3434
|
+
`route '${this.#s.key}': Cannot combine .unprotected() and .siwx() on the same route.`
|
|
3320
3435
|
);
|
|
3321
3436
|
}
|
|
3322
|
-
if (this.
|
|
3437
|
+
if (this.#s.apiKeyResolver) {
|
|
3323
3438
|
throw new Error(
|
|
3324
|
-
`route '${this.
|
|
3439
|
+
`route '${this.#s.key}': Combining .siwx() and .apiKey() is not supported on the same route.`
|
|
3325
3440
|
);
|
|
3326
3441
|
}
|
|
3327
3442
|
const next = this.fork();
|
|
3328
|
-
next.
|
|
3329
|
-
if (next.
|
|
3330
|
-
next.
|
|
3331
|
-
if (next.
|
|
3443
|
+
next.#s.siwxEnabled = true;
|
|
3444
|
+
if (next.#s.authMode === "paid" || next.#s.pricing) {
|
|
3445
|
+
next.#s.authMode = "paid";
|
|
3446
|
+
if (next.#s.protocols.length === 0) next.#s.protocols = ["x402"];
|
|
3332
3447
|
return next;
|
|
3333
3448
|
}
|
|
3334
|
-
next.
|
|
3335
|
-
next.
|
|
3449
|
+
next.#s.authMode = "siwx";
|
|
3450
|
+
next.#s.protocols = [];
|
|
3336
3451
|
return next;
|
|
3337
3452
|
}
|
|
3338
3453
|
/**
|
|
@@ -3349,14 +3464,14 @@ var RouteBuilder = class {
|
|
|
3349
3464
|
* ```
|
|
3350
3465
|
*/
|
|
3351
3466
|
apiKey(resolver) {
|
|
3352
|
-
if (this.
|
|
3467
|
+
if (this.#s.siwxEnabled) {
|
|
3353
3468
|
throw new Error(
|
|
3354
|
-
`route '${this.
|
|
3469
|
+
`route '${this.#s.key}': Combining .apiKey() and .siwx() is not supported on the same route.`
|
|
3355
3470
|
);
|
|
3356
3471
|
}
|
|
3357
3472
|
const next = this.fork();
|
|
3358
|
-
next.
|
|
3359
|
-
next.
|
|
3473
|
+
next.#s.authMode = "apiKey";
|
|
3474
|
+
next.#s.apiKeyResolver = resolver;
|
|
3360
3475
|
return next;
|
|
3361
3476
|
}
|
|
3362
3477
|
/**
|
|
@@ -3369,19 +3484,19 @@ var RouteBuilder = class {
|
|
|
3369
3484
|
* ```
|
|
3370
3485
|
*/
|
|
3371
3486
|
unprotected() {
|
|
3372
|
-
if (this.
|
|
3487
|
+
if (this.#s.authMode && this.#s.authMode !== "unprotected") {
|
|
3373
3488
|
throw new Error(
|
|
3374
|
-
`route '${this.
|
|
3489
|
+
`route '${this.#s.key}': Cannot combine .unprotected() and .${this.#s.authMode}() on the same route.`
|
|
3375
3490
|
);
|
|
3376
3491
|
}
|
|
3377
|
-
if (this.
|
|
3492
|
+
if (this.#s.pricing) {
|
|
3378
3493
|
throw new Error(
|
|
3379
|
-
`route '${this.
|
|
3494
|
+
`route '${this.#s.key}': Cannot combine .unprotected() and .paid() on the same route.`
|
|
3380
3495
|
);
|
|
3381
3496
|
}
|
|
3382
3497
|
const next = this.fork();
|
|
3383
|
-
next.
|
|
3384
|
-
next.
|
|
3498
|
+
next.#s.authMode = "unprotected";
|
|
3499
|
+
next.#s.protocols = [];
|
|
3385
3500
|
return next;
|
|
3386
3501
|
}
|
|
3387
3502
|
/**
|
|
@@ -3400,8 +3515,8 @@ var RouteBuilder = class {
|
|
|
3400
3515
|
*/
|
|
3401
3516
|
provider(name, config) {
|
|
3402
3517
|
const next = this.fork();
|
|
3403
|
-
next.
|
|
3404
|
-
next.
|
|
3518
|
+
next.#s.providerName = name;
|
|
3519
|
+
next.#s.providerConfig = config ?? {};
|
|
3405
3520
|
return next;
|
|
3406
3521
|
}
|
|
3407
3522
|
/**
|
|
@@ -3416,7 +3531,7 @@ var RouteBuilder = class {
|
|
|
3416
3531
|
*/
|
|
3417
3532
|
body(schema) {
|
|
3418
3533
|
const next = this.fork();
|
|
3419
|
-
next.
|
|
3534
|
+
next.#s.bodySchema = schema;
|
|
3420
3535
|
return next;
|
|
3421
3536
|
}
|
|
3422
3537
|
/**
|
|
@@ -3432,8 +3547,8 @@ var RouteBuilder = class {
|
|
|
3432
3547
|
*/
|
|
3433
3548
|
query(schema) {
|
|
3434
3549
|
const next = this.fork();
|
|
3435
|
-
next.
|
|
3436
|
-
next.
|
|
3550
|
+
next.#s.querySchema = schema;
|
|
3551
|
+
next.#s.method = "GET";
|
|
3437
3552
|
return next;
|
|
3438
3553
|
}
|
|
3439
3554
|
/**
|
|
@@ -3450,7 +3565,7 @@ var RouteBuilder = class {
|
|
|
3450
3565
|
*/
|
|
3451
3566
|
output(schema) {
|
|
3452
3567
|
const next = this.fork();
|
|
3453
|
-
next.
|
|
3568
|
+
next.#s.outputSchema = schema;
|
|
3454
3569
|
return next;
|
|
3455
3570
|
}
|
|
3456
3571
|
/**
|
|
@@ -3464,8 +3579,8 @@ var RouteBuilder = class {
|
|
|
3464
3579
|
*/
|
|
3465
3580
|
inputExample(example) {
|
|
3466
3581
|
const next = this.fork();
|
|
3467
|
-
next.
|
|
3468
|
-
next.
|
|
3582
|
+
next.#s.inputExample = example;
|
|
3583
|
+
next.#s.hasInputExample = true;
|
|
3469
3584
|
return next;
|
|
3470
3585
|
}
|
|
3471
3586
|
/**
|
|
@@ -3479,8 +3594,8 @@ var RouteBuilder = class {
|
|
|
3479
3594
|
*/
|
|
3480
3595
|
outputExample(example) {
|
|
3481
3596
|
const next = this.fork();
|
|
3482
|
-
next.
|
|
3483
|
-
next.
|
|
3597
|
+
next.#s.outputExample = example;
|
|
3598
|
+
next.#s.hasOutputExample = true;
|
|
3484
3599
|
return next;
|
|
3485
3600
|
}
|
|
3486
3601
|
/**
|
|
@@ -3494,7 +3609,7 @@ var RouteBuilder = class {
|
|
|
3494
3609
|
*/
|
|
3495
3610
|
description(text) {
|
|
3496
3611
|
const next = this.fork();
|
|
3497
|
-
next.
|
|
3612
|
+
next.#s.description = text;
|
|
3498
3613
|
return next;
|
|
3499
3614
|
}
|
|
3500
3615
|
/**
|
|
@@ -3508,7 +3623,7 @@ var RouteBuilder = class {
|
|
|
3508
3623
|
*/
|
|
3509
3624
|
path(p) {
|
|
3510
3625
|
const next = this.fork();
|
|
3511
|
-
next.
|
|
3626
|
+
next.#s.path = p;
|
|
3512
3627
|
return next;
|
|
3513
3628
|
}
|
|
3514
3629
|
/**
|
|
@@ -3522,7 +3637,7 @@ var RouteBuilder = class {
|
|
|
3522
3637
|
*/
|
|
3523
3638
|
method(m) {
|
|
3524
3639
|
const next = this.fork();
|
|
3525
|
-
next.
|
|
3640
|
+
next.#s.method = m;
|
|
3526
3641
|
return next;
|
|
3527
3642
|
}
|
|
3528
3643
|
/**
|
|
@@ -3541,7 +3656,7 @@ var RouteBuilder = class {
|
|
|
3541
3656
|
*/
|
|
3542
3657
|
validate(fn) {
|
|
3543
3658
|
const next = this.fork();
|
|
3544
|
-
next.
|
|
3659
|
+
next.#s.validateFn = fn;
|
|
3545
3660
|
return next;
|
|
3546
3661
|
}
|
|
3547
3662
|
/**
|
|
@@ -3559,7 +3674,7 @@ var RouteBuilder = class {
|
|
|
3559
3674
|
*/
|
|
3560
3675
|
settlement(lifecycle) {
|
|
3561
3676
|
const next = this.fork();
|
|
3562
|
-
next.
|
|
3677
|
+
next.#s.settlement = lifecycle;
|
|
3563
3678
|
return next;
|
|
3564
3679
|
}
|
|
3565
3680
|
/**
|
|
@@ -3602,79 +3717,79 @@ var RouteBuilder = class {
|
|
|
3602
3717
|
return this.register(fn, true);
|
|
3603
3718
|
}
|
|
3604
3719
|
register(handlerFn, streaming) {
|
|
3605
|
-
if (!this.
|
|
3720
|
+
if (!this.#s.authMode) {
|
|
3606
3721
|
throw new Error(
|
|
3607
|
-
`route '${this.
|
|
3722
|
+
`route '${this.#s.key}': Select an auth mode: .paid(pricing), .siwx(), .apiKey(resolver), or .unprotected()`
|
|
3608
3723
|
);
|
|
3609
3724
|
}
|
|
3610
|
-
if (this.
|
|
3725
|
+
if (this.#s.validateFn && !this.#s.bodySchema) {
|
|
3611
3726
|
throw new Error(
|
|
3612
|
-
`route '${this.
|
|
3727
|
+
`route '${this.#s.key}': .validate() requires .body() \u2014 validation runs on parsed body`
|
|
3613
3728
|
);
|
|
3614
3729
|
}
|
|
3615
|
-
if (this.
|
|
3616
|
-
throw new Error(`route '${this.
|
|
3730
|
+
if (this.#s.settlement && !this.#s.pricing) {
|
|
3731
|
+
throw new Error(`route '${this.#s.key}': .settlement() requires a paid route`);
|
|
3617
3732
|
}
|
|
3618
|
-
if (this.
|
|
3619
|
-
const hasUpto = this.
|
|
3733
|
+
if (this.#s.dynamicPrice && this.#s.protocols.includes("x402")) {
|
|
3734
|
+
const hasUpto = this.#s.deps.x402Accepts.some((accept) => accept.scheme === "upto");
|
|
3620
3735
|
if (!hasUpto) {
|
|
3621
3736
|
throw new Error(
|
|
3622
|
-
`route '${this.
|
|
3737
|
+
`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.`
|
|
3623
3738
|
);
|
|
3624
3739
|
}
|
|
3625
3740
|
}
|
|
3626
|
-
if (this.
|
|
3627
|
-
if (!this.
|
|
3741
|
+
if (this.#s.dynamicPrice && this.#s.protocols.includes("mpp")) {
|
|
3742
|
+
if (!this.#s.deps.mppSessionConfig) {
|
|
3628
3743
|
throw new Error(
|
|
3629
|
-
`route '${this.
|
|
3744
|
+
`route '${this.#s.key}': .paid({ dynamic: true }) on an MPP route requires session mode. Set RouterConfig.mpp.session = {} and provide mpp.operatorKey.`
|
|
3630
3745
|
);
|
|
3631
3746
|
}
|
|
3632
3747
|
}
|
|
3633
|
-
if (streaming && !this.
|
|
3748
|
+
if (streaming && !this.#s.dynamicPrice) {
|
|
3634
3749
|
throw new Error(
|
|
3635
|
-
`route '${this.
|
|
3750
|
+
`route '${this.#s.key}': .stream() requires .paid({ dynamic: true }) \u2014 static/free routes can't meter per-chunk billing.`
|
|
3636
3751
|
);
|
|
3637
3752
|
}
|
|
3638
3753
|
validateExamples(
|
|
3639
|
-
this.
|
|
3640
|
-
this.
|
|
3641
|
-
this.
|
|
3642
|
-
this.
|
|
3643
|
-
this.
|
|
3644
|
-
this.
|
|
3645
|
-
this.
|
|
3646
|
-
this.
|
|
3754
|
+
this.#s.key,
|
|
3755
|
+
this.#s.bodySchema,
|
|
3756
|
+
this.#s.querySchema,
|
|
3757
|
+
this.#s.outputSchema,
|
|
3758
|
+
this.#s.inputExample,
|
|
3759
|
+
this.#s.hasInputExample,
|
|
3760
|
+
this.#s.outputExample,
|
|
3761
|
+
this.#s.hasOutputExample
|
|
3647
3762
|
);
|
|
3648
3763
|
const entry = {
|
|
3649
|
-
key: this.
|
|
3650
|
-
authMode: this.
|
|
3651
|
-
siwxEnabled: this.
|
|
3652
|
-
pricing: this.
|
|
3653
|
-
dynamicPrice: this.
|
|
3764
|
+
key: this.#s.key,
|
|
3765
|
+
authMode: this.#s.authMode,
|
|
3766
|
+
siwxEnabled: this.#s.siwxEnabled,
|
|
3767
|
+
pricing: this.#s.pricing,
|
|
3768
|
+
dynamicPrice: this.#s.dynamicPrice ? true : void 0,
|
|
3654
3769
|
streaming: streaming ? true : void 0,
|
|
3655
|
-
protocols: this.
|
|
3656
|
-
bodySchema: this.
|
|
3657
|
-
querySchema: this.
|
|
3658
|
-
outputSchema: this.
|
|
3659
|
-
inputExample: this.
|
|
3660
|
-
outputExample: this.
|
|
3661
|
-
description: this.
|
|
3662
|
-
path: this.
|
|
3663
|
-
method: this.
|
|
3664
|
-
maxPrice: this.
|
|
3665
|
-
minPrice: this.
|
|
3666
|
-
payTo: this.
|
|
3667
|
-
apiKeyResolver: this.
|
|
3668
|
-
providerName: this.
|
|
3669
|
-
providerConfig: this.
|
|
3670
|
-
validateFn: this.
|
|
3671
|
-
settlement: this.
|
|
3672
|
-
mppInfo: this.
|
|
3673
|
-
tickCost: this.
|
|
3674
|
-
unitType: this.
|
|
3770
|
+
protocols: this.#s.protocols,
|
|
3771
|
+
bodySchema: this.#s.bodySchema,
|
|
3772
|
+
querySchema: this.#s.querySchema,
|
|
3773
|
+
outputSchema: this.#s.outputSchema,
|
|
3774
|
+
inputExample: this.#s.hasInputExample ? this.#s.inputExample : void 0,
|
|
3775
|
+
outputExample: this.#s.hasOutputExample ? this.#s.outputExample : void 0,
|
|
3776
|
+
description: this.#s.description,
|
|
3777
|
+
path: this.#s.path,
|
|
3778
|
+
method: this.#s.method,
|
|
3779
|
+
maxPrice: this.#s.maxPrice,
|
|
3780
|
+
minPrice: this.#s.minPrice,
|
|
3781
|
+
payTo: this.#s.payTo,
|
|
3782
|
+
apiKeyResolver: this.#s.apiKeyResolver,
|
|
3783
|
+
providerName: this.#s.providerName,
|
|
3784
|
+
providerConfig: this.#s.providerConfig,
|
|
3785
|
+
validateFn: this.#s.validateFn,
|
|
3786
|
+
settlement: this.#s.settlement,
|
|
3787
|
+
mppInfo: this.#s.mppInfo,
|
|
3788
|
+
tickCost: this.#s.tickCost,
|
|
3789
|
+
unitType: this.#s.unitType
|
|
3675
3790
|
};
|
|
3676
|
-
this.
|
|
3677
|
-
return createRequestHandler(entry, handlerFn, this.
|
|
3791
|
+
this.#s.registry.register(entry);
|
|
3792
|
+
return createRequestHandler(entry, handlerFn, this.#s.deps);
|
|
3678
3793
|
}
|
|
3679
3794
|
};
|
|
3680
3795
|
function resolvePaidArgs(routeKey, pricingOrOptions, options) {
|
|
@@ -3918,17 +4033,16 @@ function buildPricingInfo(entry) {
|
|
|
3918
4033
|
};
|
|
3919
4034
|
}
|
|
3920
4035
|
if ("tiers" in entry.pricing) {
|
|
3921
|
-
const tierPrices = Object.values(entry.pricing.tiers).map((tier) =>
|
|
3922
|
-
const
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
if (min === max) {
|
|
4036
|
+
const tierPrices = Object.values(entry.pricing.tiers).map((tier) => tier.price);
|
|
4037
|
+
const extrema = tierExtrema(tierPrices);
|
|
4038
|
+
if (extrema) {
|
|
4039
|
+
if (extrema.min === extrema.max) {
|
|
3926
4040
|
return {
|
|
3927
|
-
price: { mode: "fixed", currency: "USD", amount:
|
|
4041
|
+
price: { mode: "fixed", currency: "USD", amount: extrema.min }
|
|
3928
4042
|
};
|
|
3929
4043
|
}
|
|
3930
4044
|
return {
|
|
3931
|
-
price: { mode: "dynamic", currency: "USD", min:
|
|
4045
|
+
price: { mode: "dynamic", currency: "USD", min: extrema.min, max: extrema.max }
|
|
3932
4046
|
};
|
|
3933
4047
|
}
|
|
3934
4048
|
return {
|
|
@@ -3942,6 +4056,20 @@ function buildPricingInfo(entry) {
|
|
|
3942
4056
|
}
|
|
3943
4057
|
return void 0;
|
|
3944
4058
|
}
|
|
4059
|
+
function tierExtrema(prices) {
|
|
4060
|
+
if (prices.length === 0) return null;
|
|
4061
|
+
let min = prices[0];
|
|
4062
|
+
let max = prices[0];
|
|
4063
|
+
try {
|
|
4064
|
+
for (const price of prices.slice(1)) {
|
|
4065
|
+
if (compareDecimals(price, min) < 0) min = price;
|
|
4066
|
+
if (compareDecimals(price, max) > 0) max = price;
|
|
4067
|
+
}
|
|
4068
|
+
} catch {
|
|
4069
|
+
return null;
|
|
4070
|
+
}
|
|
4071
|
+
return { min, max };
|
|
4072
|
+
}
|
|
3945
4073
|
|
|
3946
4074
|
// src/discovery/llms-txt.ts
|
|
3947
4075
|
var import_server10 = require("next/server");
|
|
@@ -4428,11 +4556,11 @@ function getRouterConfigIssues(config, options = {}) {
|
|
|
4428
4556
|
}
|
|
4429
4557
|
|
|
4430
4558
|
// src/init/x402.ts
|
|
4431
|
-
async function initX402(config, configError) {
|
|
4559
|
+
async function initX402(config, kvStore, configError) {
|
|
4432
4560
|
if (configError) return { initError: configError };
|
|
4433
4561
|
try {
|
|
4434
4562
|
const { createX402Server: createX402Server2 } = await Promise.resolve().then(() => (init_x402_server(), x402_server_exports));
|
|
4435
|
-
const result = await createX402Server2(config);
|
|
4563
|
+
const result = await createX402Server2(config, kvStore);
|
|
4436
4564
|
await result.initPromise;
|
|
4437
4565
|
return {
|
|
4438
4566
|
server: result.server,
|
|
@@ -4615,7 +4743,7 @@ function createRouter(config) {
|
|
|
4615
4743
|
mppSessionConfig: config.mpp?.session ? { depositMultiplier: config.mpp.session.depositMultiplier ?? 10 } : null
|
|
4616
4744
|
};
|
|
4617
4745
|
deps.initPromise = (async () => {
|
|
4618
|
-
const x402Result = await initX402(config, x402ConfigError);
|
|
4746
|
+
const x402Result = await initX402(config, kvStore, x402ConfigError);
|
|
4619
4747
|
deps.x402Server = x402Result.server ?? null;
|
|
4620
4748
|
deps.x402FacilitatorsByNetwork = x402Result.facilitatorsByNetwork;
|
|
4621
4749
|
if (x402Result.initError) deps.x402InitError = x402Result.initError;
|
|
@@ -4644,11 +4772,10 @@ function createRouter(config) {
|
|
|
4644
4772
|
`[router] strictRoutes=true forbids key/path divergence for route '${definition.path}'. Remove custom \`key\` or make it equal to \`path\`.`
|
|
4645
4773
|
);
|
|
4646
4774
|
}
|
|
4647
|
-
let builder = new RouteBuilder(key, registry, deps
|
|
4775
|
+
let builder = new RouteBuilder(key, registry, deps, {
|
|
4776
|
+
protocols: config.protocols
|
|
4777
|
+
});
|
|
4648
4778
|
builder = builder.path(normalizedPath);
|
|
4649
|
-
if (config.protocols) {
|
|
4650
|
-
builder._protocols = [...config.protocols];
|
|
4651
|
-
}
|
|
4652
4779
|
if (definition.method) {
|
|
4653
4780
|
builder = builder.method(definition.method);
|
|
4654
4781
|
}
|