@agentcash/router 0.7.1 → 1.0.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 +74 -42
- package/dist/index.d.cts +21 -31
- package/dist/index.d.ts +21 -31
- package/dist/index.js +74 -42
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -90,33 +90,46 @@ module.exports = __toCommonJS(index_exports);
|
|
|
90
90
|
// src/registry.ts
|
|
91
91
|
var RouteRegistry = class {
|
|
92
92
|
routes = /* @__PURE__ */ new Map();
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
93
|
+
// Internal map key includes the HTTP method so that POST and DELETE on the
|
|
94
|
+
// same path coexist. Within the same path+method, last-write-wins is still
|
|
95
|
+
// intentional — Next.js module loading order is non-deterministic during
|
|
96
|
+
// build and discovery stubs may register the same route in either order.
|
|
96
97
|
// Prior art: ElysiaJS uses the same pattern (silent overwrite in router.history).
|
|
98
|
+
mapKey(entry) {
|
|
99
|
+
return `${entry.key}:${entry.method}`;
|
|
100
|
+
}
|
|
97
101
|
register(entry) {
|
|
98
|
-
|
|
102
|
+
const k = this.mapKey(entry);
|
|
103
|
+
if (this.routes.has(k) && typeof process !== "undefined" && process.env?.["NODE_ENV"] !== "production") {
|
|
99
104
|
console.warn(
|
|
100
|
-
`[agentcash/router] route '${entry.key}' registered twice \u2014 overwriting (this is expected for discovery stubs during next build)`
|
|
105
|
+
`[agentcash/router] route '${entry.key}' (${entry.method}) registered twice \u2014 overwriting (this is expected for discovery stubs during next build)`
|
|
101
106
|
);
|
|
102
107
|
}
|
|
103
|
-
this.routes.set(
|
|
108
|
+
this.routes.set(k, entry);
|
|
104
109
|
}
|
|
110
|
+
// Accepts either a compound key ("site/domain:DELETE") or a path-only key
|
|
111
|
+
// ("site/domain") — path-only returns the first registered method for that path.
|
|
105
112
|
get(key) {
|
|
106
|
-
|
|
113
|
+
const direct = this.routes.get(key);
|
|
114
|
+
if (direct) return direct;
|
|
115
|
+
for (const entry of this.routes.values()) {
|
|
116
|
+
if (entry.key === key) return entry;
|
|
117
|
+
}
|
|
118
|
+
return void 0;
|
|
107
119
|
}
|
|
108
120
|
entries() {
|
|
109
121
|
return this.routes.entries();
|
|
110
122
|
}
|
|
111
123
|
has(key) {
|
|
112
|
-
return this.
|
|
124
|
+
return this.get(key) !== void 0;
|
|
113
125
|
}
|
|
114
126
|
get size() {
|
|
115
127
|
return this.routes.size;
|
|
116
128
|
}
|
|
117
129
|
validate(expectedKeys) {
|
|
118
130
|
if (!expectedKeys) return;
|
|
119
|
-
const
|
|
131
|
+
const registeredPathKeys = new Set([...this.routes.values()].map((e) => e.key));
|
|
132
|
+
const missing = expectedKeys.filter((k) => !registeredPathKeys.has(k));
|
|
120
133
|
if (missing.length > 0) {
|
|
121
134
|
throw new Error(
|
|
122
135
|
`route${missing.length > 1 ? "s" : ""} ${missing.map((k) => `'${k}'`).join(", ")} in prices map but not registered \u2014 add to barrel imports`
|
|
@@ -1456,7 +1469,15 @@ function createRedisEntitlementStore(client, options) {
|
|
|
1456
1469
|
|
|
1457
1470
|
// src/discovery/well-known.ts
|
|
1458
1471
|
var import_server3 = require("next/server");
|
|
1459
|
-
|
|
1472
|
+
|
|
1473
|
+
// src/discovery/utils/guidance.ts
|
|
1474
|
+
async function resolveGuidance(discovery) {
|
|
1475
|
+
if (typeof discovery.guidance === "function") return discovery.guidance();
|
|
1476
|
+
return discovery.guidance;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// src/discovery/well-known.ts
|
|
1480
|
+
function createWellKnownHandler(registry, baseUrl, pricesKeys, discovery) {
|
|
1460
1481
|
const normalizedBase = baseUrl.replace(/\/+$/, "");
|
|
1461
1482
|
let validated = false;
|
|
1462
1483
|
return async (_request) => {
|
|
@@ -1466,19 +1487,14 @@ function createWellKnownHandler(registry, baseUrl, pricesKeys, options = {}) {
|
|
|
1466
1487
|
}
|
|
1467
1488
|
const x402Set = /* @__PURE__ */ new Set();
|
|
1468
1489
|
const mppSet = /* @__PURE__ */ new Set();
|
|
1469
|
-
const methodHints =
|
|
1470
|
-
for (const [
|
|
1471
|
-
const url = `${normalizedBase}/api/${entry.path ?? key}`;
|
|
1490
|
+
const methodHints = discovery.methodHints ?? "non-default";
|
|
1491
|
+
for (const [, entry] of registry.entries()) {
|
|
1492
|
+
const url = `${normalizedBase}/api/${entry.path ?? entry.key}`;
|
|
1472
1493
|
const resource = toDiscoveryResource(entry.method, url, methodHints);
|
|
1473
1494
|
if (entry.authMode !== "unprotected") x402Set.add(resource);
|
|
1474
1495
|
if (entry.protocols.includes("mpp")) mppSet.add(resource);
|
|
1475
1496
|
}
|
|
1476
|
-
|
|
1477
|
-
if (typeof options.instructions === "function") {
|
|
1478
|
-
instructions = await options.instructions();
|
|
1479
|
-
} else if (typeof options.instructions === "string") {
|
|
1480
|
-
instructions = options.instructions;
|
|
1481
|
-
}
|
|
1497
|
+
const instructions = await resolveGuidance(discovery);
|
|
1482
1498
|
const body = {
|
|
1483
1499
|
version: 1,
|
|
1484
1500
|
resources: Array.from(x402Set)
|
|
@@ -1487,11 +1503,11 @@ function createWellKnownHandler(registry, baseUrl, pricesKeys, options = {}) {
|
|
|
1487
1503
|
if (mppResources.length > 0) {
|
|
1488
1504
|
body.mppResources = mppResources;
|
|
1489
1505
|
}
|
|
1490
|
-
if (
|
|
1491
|
-
body.description =
|
|
1506
|
+
if (discovery.description) {
|
|
1507
|
+
body.description = discovery.description;
|
|
1492
1508
|
}
|
|
1493
|
-
if (
|
|
1494
|
-
body.ownershipProofs =
|
|
1509
|
+
if (discovery.ownershipProofs) {
|
|
1510
|
+
body.ownershipProofs = discovery.ownershipProofs;
|
|
1495
1511
|
}
|
|
1496
1512
|
if (instructions) {
|
|
1497
1513
|
body.instructions = instructions;
|
|
@@ -1514,7 +1530,7 @@ function toDiscoveryResource(method, url, mode) {
|
|
|
1514
1530
|
|
|
1515
1531
|
// src/discovery/openapi.ts
|
|
1516
1532
|
var import_server4 = require("next/server");
|
|
1517
|
-
function createOpenAPIHandler(registry, baseUrl, pricesKeys,
|
|
1533
|
+
function createOpenAPIHandler(registry, baseUrl, pricesKeys, discovery) {
|
|
1518
1534
|
const normalizedBase = baseUrl.replace(/\/+$/, "");
|
|
1519
1535
|
let cached = null;
|
|
1520
1536
|
let validated = false;
|
|
@@ -1529,12 +1545,12 @@ function createOpenAPIHandler(registry, baseUrl, pricesKeys, options) {
|
|
|
1529
1545
|
const tagSet = /* @__PURE__ */ new Set();
|
|
1530
1546
|
let requiresSiwxScheme = false;
|
|
1531
1547
|
let requiresApiKeyScheme = false;
|
|
1532
|
-
for (const [
|
|
1533
|
-
const apiPath = `/api/${entry.path ?? key}`;
|
|
1548
|
+
for (const [, entry] of registry.entries()) {
|
|
1549
|
+
const apiPath = `/api/${entry.path ?? entry.key}`;
|
|
1534
1550
|
const method = entry.method.toLowerCase();
|
|
1535
|
-
const tag = deriveTag(key);
|
|
1551
|
+
const tag = deriveTag(entry.key);
|
|
1536
1552
|
tagSet.add(tag);
|
|
1537
|
-
const built = buildOperation(key, entry, tag);
|
|
1553
|
+
const built = buildOperation(entry.key, entry, tag);
|
|
1538
1554
|
if (built.requiresSiwxScheme) requiresSiwxScheme = true;
|
|
1539
1555
|
if (built.requiresApiKeyScheme) requiresApiKeyScheme = true;
|
|
1540
1556
|
paths[apiPath] = { ...paths[apiPath], [method]: built.operation };
|
|
@@ -1555,21 +1571,20 @@ function createOpenAPIHandler(registry, baseUrl, pricesKeys, options) {
|
|
|
1555
1571
|
};
|
|
1556
1572
|
}
|
|
1557
1573
|
const discoveryMetadata = {};
|
|
1558
|
-
if (
|
|
1559
|
-
discoveryMetadata.ownershipProofs =
|
|
1560
|
-
}
|
|
1561
|
-
if (options.llmsTxtUrl) {
|
|
1562
|
-
discoveryMetadata.llmsTxtUrl = options.llmsTxtUrl;
|
|
1574
|
+
if (discovery.ownershipProofs && discovery.ownershipProofs.length > 0) {
|
|
1575
|
+
discoveryMetadata.ownershipProofs = discovery.ownershipProofs;
|
|
1563
1576
|
}
|
|
1577
|
+
const guidance = await resolveGuidance(discovery);
|
|
1564
1578
|
const openApiDocument = {
|
|
1565
1579
|
openapi: "3.1.0",
|
|
1566
1580
|
info: {
|
|
1567
|
-
title:
|
|
1568
|
-
description:
|
|
1569
|
-
version:
|
|
1570
|
-
|
|
1581
|
+
title: discovery.title,
|
|
1582
|
+
description: discovery.description,
|
|
1583
|
+
version: discovery.version,
|
|
1584
|
+
guidance,
|
|
1585
|
+
...discovery.contact && { contact: discovery.contact }
|
|
1571
1586
|
},
|
|
1572
|
-
servers: [{ url: (
|
|
1587
|
+
servers: [{ url: (discovery.serverUrl ?? normalizedBase).replace(/\/+$/, "") }],
|
|
1573
1588
|
tags: Array.from(tagSet).sort().map((name) => ({ name })),
|
|
1574
1589
|
...Object.keys(securitySchemes).length > 0 ? {
|
|
1575
1590
|
components: {
|
|
@@ -1689,6 +1704,20 @@ function buildPricingInfo(entry) {
|
|
|
1689
1704
|
return void 0;
|
|
1690
1705
|
}
|
|
1691
1706
|
|
|
1707
|
+
// src/discovery/llms-txt.ts
|
|
1708
|
+
var import_server5 = require("next/server");
|
|
1709
|
+
function createLlmsTxtHandler(discovery) {
|
|
1710
|
+
return async (_request) => {
|
|
1711
|
+
const guidance = await resolveGuidance(discovery) ?? "";
|
|
1712
|
+
return new import_server5.NextResponse(guidance, {
|
|
1713
|
+
headers: {
|
|
1714
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
1715
|
+
"Access-Control-Allow-Origin": "*"
|
|
1716
|
+
}
|
|
1717
|
+
});
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1692
1721
|
// src/index.ts
|
|
1693
1722
|
function createRouter(config) {
|
|
1694
1723
|
const registry = new RouteRegistry();
|
|
@@ -1817,11 +1846,14 @@ function createRouter(config) {
|
|
|
1817
1846
|
}
|
|
1818
1847
|
return builder;
|
|
1819
1848
|
},
|
|
1820
|
-
wellKnown(
|
|
1821
|
-
return createWellKnownHandler(registry, resolvedBaseUrl, pricesKeys,
|
|
1849
|
+
wellKnown() {
|
|
1850
|
+
return createWellKnownHandler(registry, resolvedBaseUrl, pricesKeys, config.discovery);
|
|
1851
|
+
},
|
|
1852
|
+
openapi() {
|
|
1853
|
+
return createOpenAPIHandler(registry, resolvedBaseUrl, pricesKeys, config.discovery);
|
|
1822
1854
|
},
|
|
1823
|
-
|
|
1824
|
-
return
|
|
1855
|
+
llmsTxt() {
|
|
1856
|
+
return createLlmsTxtHandler(config.discovery);
|
|
1825
1857
|
},
|
|
1826
1858
|
monitors() {
|
|
1827
1859
|
const result = [];
|
package/dist/index.d.cts
CHANGED
|
@@ -280,6 +280,21 @@ interface RouteEntry {
|
|
|
280
280
|
providerConfig?: ProviderConfig;
|
|
281
281
|
validateFn?: (body: unknown) => void | Promise<void>;
|
|
282
282
|
}
|
|
283
|
+
interface DiscoveryConfig {
|
|
284
|
+
title: string;
|
|
285
|
+
version: string;
|
|
286
|
+
description?: string;
|
|
287
|
+
contact?: {
|
|
288
|
+
name?: string;
|
|
289
|
+
url?: string;
|
|
290
|
+
};
|
|
291
|
+
ownershipProofs?: string[];
|
|
292
|
+
methodHints?: 'off' | 'non-default' | 'always';
|
|
293
|
+
/** Natural language guidance for agents. Served as wellknown `instructions` and `/llms.txt`. */
|
|
294
|
+
guidance?: string | (() => string | Promise<string>);
|
|
295
|
+
/** Override the OpenAPI `servers` URL. Defaults to `RouterConfig.baseUrl`. Use when the public API hostname differs from the payment realm URL. */
|
|
296
|
+
serverUrl?: string;
|
|
297
|
+
}
|
|
283
298
|
interface RouterConfig {
|
|
284
299
|
payeeAddress: string;
|
|
285
300
|
/**
|
|
@@ -329,10 +344,12 @@ interface RouterConfig {
|
|
|
329
344
|
* This prevents discovery/openapi drift caused by shorthand internal keys.
|
|
330
345
|
*/
|
|
331
346
|
strictRoutes?: boolean;
|
|
347
|
+
discovery: DiscoveryConfig;
|
|
332
348
|
}
|
|
333
349
|
|
|
334
350
|
declare class RouteRegistry {
|
|
335
351
|
private routes;
|
|
352
|
+
private mapKey;
|
|
336
353
|
register(entry: RouteEntry): void;
|
|
337
354
|
get(key: string): RouteEntry | undefined;
|
|
338
355
|
entries(): IterableIterator<[string, RouteEntry]>;
|
|
@@ -341,34 +358,6 @@ declare class RouteRegistry {
|
|
|
341
358
|
validate(expectedKeys?: string[]): void;
|
|
342
359
|
}
|
|
343
360
|
|
|
344
|
-
interface WellKnownOptions {
|
|
345
|
-
description?: string;
|
|
346
|
-
instructions?: string | (() => string | Promise<string>);
|
|
347
|
-
ownershipProofs?: string[];
|
|
348
|
-
/**
|
|
349
|
-
* Whether to include explicit HTTP method prefixes in `resources`.
|
|
350
|
-
* - `off`: always emit plain URLs.
|
|
351
|
-
* - `non-default`: emit `METHOD url` for PUT/PATCH/DELETE routes.
|
|
352
|
-
* - `always`: emit `METHOD url` for all routes.
|
|
353
|
-
*
|
|
354
|
-
* @default 'non-default'
|
|
355
|
-
*/
|
|
356
|
-
methodHints?: 'off' | 'non-default' | 'always';
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
interface OpenAPIOptions {
|
|
360
|
-
title: string;
|
|
361
|
-
version: string;
|
|
362
|
-
description?: string;
|
|
363
|
-
baseUrl?: string;
|
|
364
|
-
contact?: {
|
|
365
|
-
name?: string;
|
|
366
|
-
url?: string;
|
|
367
|
-
};
|
|
368
|
-
llmsTxtUrl?: string;
|
|
369
|
-
ownershipProofs?: string[];
|
|
370
|
-
}
|
|
371
|
-
|
|
372
361
|
interface OrchestrateDeps {
|
|
373
362
|
x402Server: X402Server | null;
|
|
374
363
|
initPromise: Promise<void>;
|
|
@@ -477,8 +466,9 @@ interface MonitorEntry {
|
|
|
477
466
|
}
|
|
478
467
|
interface ServiceRouter<TPriceKeys extends string = never> {
|
|
479
468
|
route<K extends string>(keyOrDefinition: K | RouteDefinition<K>): [K] extends [TPriceKeys] ? RouteBuilder<undefined, undefined, true, false, false> : RouteBuilder<undefined, undefined, false, false, false>;
|
|
480
|
-
wellKnown(
|
|
481
|
-
openapi(
|
|
469
|
+
wellKnown(): (request: NextRequest) => Promise<NextResponse>;
|
|
470
|
+
openapi(): (request: NextRequest) => Promise<NextResponse>;
|
|
471
|
+
llmsTxt(): (request: NextRequest) => Promise<NextResponse>;
|
|
482
472
|
monitors(): MonitorEntry[];
|
|
483
473
|
registry: RouteRegistry;
|
|
484
474
|
}
|
|
@@ -486,4 +476,4 @@ declare function createRouter<const P extends Record<string, string> = Record<ne
|
|
|
486
476
|
prices?: P;
|
|
487
477
|
}): ServiceRouter<Extract<keyof P, string>>;
|
|
488
478
|
|
|
489
|
-
export { type AlertEvent, type AlertFn, type AlertLevel, type AuthEvent, type AuthMode, type EntitlementStore, type ErrorEvent, type HandlerContext, HttpError, MemoryEntitlementStore, MemoryNonceStore, type MonitorEntry, type NonceStore, type OveragePolicy, type PaidOptions, type PaymentEvent, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RedisEntitlementStoreOptions, type RedisNonceStoreOptions, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, type RouterPlugin, SIWX_CHALLENGE_EXPIRY_MS, type ServiceRouter, type SettlementEvent, type TierConfig, type X402Server, consolePlugin, createRedisEntitlementStore, createRedisNonceStore, createRouter };
|
|
479
|
+
export { type AlertEvent, type AlertFn, type AlertLevel, type AuthEvent, type AuthMode, type DiscoveryConfig, type EntitlementStore, type ErrorEvent, type HandlerContext, HttpError, MemoryEntitlementStore, MemoryNonceStore, type MonitorEntry, type NonceStore, type OveragePolicy, type PaidOptions, type PaymentEvent, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RedisEntitlementStoreOptions, type RedisNonceStoreOptions, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, type RouterPlugin, SIWX_CHALLENGE_EXPIRY_MS, type ServiceRouter, type SettlementEvent, type TierConfig, type X402Server, consolePlugin, createRedisEntitlementStore, createRedisNonceStore, createRouter };
|
package/dist/index.d.ts
CHANGED
|
@@ -280,6 +280,21 @@ interface RouteEntry {
|
|
|
280
280
|
providerConfig?: ProviderConfig;
|
|
281
281
|
validateFn?: (body: unknown) => void | Promise<void>;
|
|
282
282
|
}
|
|
283
|
+
interface DiscoveryConfig {
|
|
284
|
+
title: string;
|
|
285
|
+
version: string;
|
|
286
|
+
description?: string;
|
|
287
|
+
contact?: {
|
|
288
|
+
name?: string;
|
|
289
|
+
url?: string;
|
|
290
|
+
};
|
|
291
|
+
ownershipProofs?: string[];
|
|
292
|
+
methodHints?: 'off' | 'non-default' | 'always';
|
|
293
|
+
/** Natural language guidance for agents. Served as wellknown `instructions` and `/llms.txt`. */
|
|
294
|
+
guidance?: string | (() => string | Promise<string>);
|
|
295
|
+
/** Override the OpenAPI `servers` URL. Defaults to `RouterConfig.baseUrl`. Use when the public API hostname differs from the payment realm URL. */
|
|
296
|
+
serverUrl?: string;
|
|
297
|
+
}
|
|
283
298
|
interface RouterConfig {
|
|
284
299
|
payeeAddress: string;
|
|
285
300
|
/**
|
|
@@ -329,10 +344,12 @@ interface RouterConfig {
|
|
|
329
344
|
* This prevents discovery/openapi drift caused by shorthand internal keys.
|
|
330
345
|
*/
|
|
331
346
|
strictRoutes?: boolean;
|
|
347
|
+
discovery: DiscoveryConfig;
|
|
332
348
|
}
|
|
333
349
|
|
|
334
350
|
declare class RouteRegistry {
|
|
335
351
|
private routes;
|
|
352
|
+
private mapKey;
|
|
336
353
|
register(entry: RouteEntry): void;
|
|
337
354
|
get(key: string): RouteEntry | undefined;
|
|
338
355
|
entries(): IterableIterator<[string, RouteEntry]>;
|
|
@@ -341,34 +358,6 @@ declare class RouteRegistry {
|
|
|
341
358
|
validate(expectedKeys?: string[]): void;
|
|
342
359
|
}
|
|
343
360
|
|
|
344
|
-
interface WellKnownOptions {
|
|
345
|
-
description?: string;
|
|
346
|
-
instructions?: string | (() => string | Promise<string>);
|
|
347
|
-
ownershipProofs?: string[];
|
|
348
|
-
/**
|
|
349
|
-
* Whether to include explicit HTTP method prefixes in `resources`.
|
|
350
|
-
* - `off`: always emit plain URLs.
|
|
351
|
-
* - `non-default`: emit `METHOD url` for PUT/PATCH/DELETE routes.
|
|
352
|
-
* - `always`: emit `METHOD url` for all routes.
|
|
353
|
-
*
|
|
354
|
-
* @default 'non-default'
|
|
355
|
-
*/
|
|
356
|
-
methodHints?: 'off' | 'non-default' | 'always';
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
interface OpenAPIOptions {
|
|
360
|
-
title: string;
|
|
361
|
-
version: string;
|
|
362
|
-
description?: string;
|
|
363
|
-
baseUrl?: string;
|
|
364
|
-
contact?: {
|
|
365
|
-
name?: string;
|
|
366
|
-
url?: string;
|
|
367
|
-
};
|
|
368
|
-
llmsTxtUrl?: string;
|
|
369
|
-
ownershipProofs?: string[];
|
|
370
|
-
}
|
|
371
|
-
|
|
372
361
|
interface OrchestrateDeps {
|
|
373
362
|
x402Server: X402Server | null;
|
|
374
363
|
initPromise: Promise<void>;
|
|
@@ -477,8 +466,9 @@ interface MonitorEntry {
|
|
|
477
466
|
}
|
|
478
467
|
interface ServiceRouter<TPriceKeys extends string = never> {
|
|
479
468
|
route<K extends string>(keyOrDefinition: K | RouteDefinition<K>): [K] extends [TPriceKeys] ? RouteBuilder<undefined, undefined, true, false, false> : RouteBuilder<undefined, undefined, false, false, false>;
|
|
480
|
-
wellKnown(
|
|
481
|
-
openapi(
|
|
469
|
+
wellKnown(): (request: NextRequest) => Promise<NextResponse>;
|
|
470
|
+
openapi(): (request: NextRequest) => Promise<NextResponse>;
|
|
471
|
+
llmsTxt(): (request: NextRequest) => Promise<NextResponse>;
|
|
482
472
|
monitors(): MonitorEntry[];
|
|
483
473
|
registry: RouteRegistry;
|
|
484
474
|
}
|
|
@@ -486,4 +476,4 @@ declare function createRouter<const P extends Record<string, string> = Record<ne
|
|
|
486
476
|
prices?: P;
|
|
487
477
|
}): ServiceRouter<Extract<keyof P, string>>;
|
|
488
478
|
|
|
489
|
-
export { type AlertEvent, type AlertFn, type AlertLevel, type AuthEvent, type AuthMode, type EntitlementStore, type ErrorEvent, type HandlerContext, HttpError, MemoryEntitlementStore, MemoryNonceStore, type MonitorEntry, type NonceStore, type OveragePolicy, type PaidOptions, type PaymentEvent, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RedisEntitlementStoreOptions, type RedisNonceStoreOptions, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, type RouterPlugin, SIWX_CHALLENGE_EXPIRY_MS, type ServiceRouter, type SettlementEvent, type TierConfig, type X402Server, consolePlugin, createRedisEntitlementStore, createRedisNonceStore, createRouter };
|
|
479
|
+
export { type AlertEvent, type AlertFn, type AlertLevel, type AuthEvent, type AuthMode, type DiscoveryConfig, type EntitlementStore, type ErrorEvent, type HandlerContext, HttpError, MemoryEntitlementStore, MemoryNonceStore, type MonitorEntry, type NonceStore, type OveragePolicy, type PaidOptions, type PaymentEvent, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RedisEntitlementStoreOptions, type RedisNonceStoreOptions, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, type RouterPlugin, SIWX_CHALLENGE_EXPIRY_MS, type ServiceRouter, type SettlementEvent, type TierConfig, type X402Server, consolePlugin, createRedisEntitlementStore, createRedisNonceStore, createRouter };
|
package/dist/index.js
CHANGED
|
@@ -51,33 +51,46 @@ var init_server = __esm({
|
|
|
51
51
|
// src/registry.ts
|
|
52
52
|
var RouteRegistry = class {
|
|
53
53
|
routes = /* @__PURE__ */ new Map();
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
54
|
+
// Internal map key includes the HTTP method so that POST and DELETE on the
|
|
55
|
+
// same path coexist. Within the same path+method, last-write-wins is still
|
|
56
|
+
// intentional — Next.js module loading order is non-deterministic during
|
|
57
|
+
// build and discovery stubs may register the same route in either order.
|
|
57
58
|
// Prior art: ElysiaJS uses the same pattern (silent overwrite in router.history).
|
|
59
|
+
mapKey(entry) {
|
|
60
|
+
return `${entry.key}:${entry.method}`;
|
|
61
|
+
}
|
|
58
62
|
register(entry) {
|
|
59
|
-
|
|
63
|
+
const k = this.mapKey(entry);
|
|
64
|
+
if (this.routes.has(k) && typeof process !== "undefined" && process.env?.["NODE_ENV"] !== "production") {
|
|
60
65
|
console.warn(
|
|
61
|
-
`[agentcash/router] route '${entry.key}' registered twice \u2014 overwriting (this is expected for discovery stubs during next build)`
|
|
66
|
+
`[agentcash/router] route '${entry.key}' (${entry.method}) registered twice \u2014 overwriting (this is expected for discovery stubs during next build)`
|
|
62
67
|
);
|
|
63
68
|
}
|
|
64
|
-
this.routes.set(
|
|
69
|
+
this.routes.set(k, entry);
|
|
65
70
|
}
|
|
71
|
+
// Accepts either a compound key ("site/domain:DELETE") or a path-only key
|
|
72
|
+
// ("site/domain") — path-only returns the first registered method for that path.
|
|
66
73
|
get(key) {
|
|
67
|
-
|
|
74
|
+
const direct = this.routes.get(key);
|
|
75
|
+
if (direct) return direct;
|
|
76
|
+
for (const entry of this.routes.values()) {
|
|
77
|
+
if (entry.key === key) return entry;
|
|
78
|
+
}
|
|
79
|
+
return void 0;
|
|
68
80
|
}
|
|
69
81
|
entries() {
|
|
70
82
|
return this.routes.entries();
|
|
71
83
|
}
|
|
72
84
|
has(key) {
|
|
73
|
-
return this.
|
|
85
|
+
return this.get(key) !== void 0;
|
|
74
86
|
}
|
|
75
87
|
get size() {
|
|
76
88
|
return this.routes.size;
|
|
77
89
|
}
|
|
78
90
|
validate(expectedKeys) {
|
|
79
91
|
if (!expectedKeys) return;
|
|
80
|
-
const
|
|
92
|
+
const registeredPathKeys = new Set([...this.routes.values()].map((e) => e.key));
|
|
93
|
+
const missing = expectedKeys.filter((k) => !registeredPathKeys.has(k));
|
|
81
94
|
if (missing.length > 0) {
|
|
82
95
|
throw new Error(
|
|
83
96
|
`route${missing.length > 1 ? "s" : ""} ${missing.map((k) => `'${k}'`).join(", ")} in prices map but not registered \u2014 add to barrel imports`
|
|
@@ -1417,7 +1430,15 @@ function createRedisEntitlementStore(client, options) {
|
|
|
1417
1430
|
|
|
1418
1431
|
// src/discovery/well-known.ts
|
|
1419
1432
|
import { NextResponse as NextResponse3 } from "next/server";
|
|
1420
|
-
|
|
1433
|
+
|
|
1434
|
+
// src/discovery/utils/guidance.ts
|
|
1435
|
+
async function resolveGuidance(discovery) {
|
|
1436
|
+
if (typeof discovery.guidance === "function") return discovery.guidance();
|
|
1437
|
+
return discovery.guidance;
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
// src/discovery/well-known.ts
|
|
1441
|
+
function createWellKnownHandler(registry, baseUrl, pricesKeys, discovery) {
|
|
1421
1442
|
const normalizedBase = baseUrl.replace(/\/+$/, "");
|
|
1422
1443
|
let validated = false;
|
|
1423
1444
|
return async (_request) => {
|
|
@@ -1427,19 +1448,14 @@ function createWellKnownHandler(registry, baseUrl, pricesKeys, options = {}) {
|
|
|
1427
1448
|
}
|
|
1428
1449
|
const x402Set = /* @__PURE__ */ new Set();
|
|
1429
1450
|
const mppSet = /* @__PURE__ */ new Set();
|
|
1430
|
-
const methodHints =
|
|
1431
|
-
for (const [
|
|
1432
|
-
const url = `${normalizedBase}/api/${entry.path ?? key}`;
|
|
1451
|
+
const methodHints = discovery.methodHints ?? "non-default";
|
|
1452
|
+
for (const [, entry] of registry.entries()) {
|
|
1453
|
+
const url = `${normalizedBase}/api/${entry.path ?? entry.key}`;
|
|
1433
1454
|
const resource = toDiscoveryResource(entry.method, url, methodHints);
|
|
1434
1455
|
if (entry.authMode !== "unprotected") x402Set.add(resource);
|
|
1435
1456
|
if (entry.protocols.includes("mpp")) mppSet.add(resource);
|
|
1436
1457
|
}
|
|
1437
|
-
|
|
1438
|
-
if (typeof options.instructions === "function") {
|
|
1439
|
-
instructions = await options.instructions();
|
|
1440
|
-
} else if (typeof options.instructions === "string") {
|
|
1441
|
-
instructions = options.instructions;
|
|
1442
|
-
}
|
|
1458
|
+
const instructions = await resolveGuidance(discovery);
|
|
1443
1459
|
const body = {
|
|
1444
1460
|
version: 1,
|
|
1445
1461
|
resources: Array.from(x402Set)
|
|
@@ -1448,11 +1464,11 @@ function createWellKnownHandler(registry, baseUrl, pricesKeys, options = {}) {
|
|
|
1448
1464
|
if (mppResources.length > 0) {
|
|
1449
1465
|
body.mppResources = mppResources;
|
|
1450
1466
|
}
|
|
1451
|
-
if (
|
|
1452
|
-
body.description =
|
|
1467
|
+
if (discovery.description) {
|
|
1468
|
+
body.description = discovery.description;
|
|
1453
1469
|
}
|
|
1454
|
-
if (
|
|
1455
|
-
body.ownershipProofs =
|
|
1470
|
+
if (discovery.ownershipProofs) {
|
|
1471
|
+
body.ownershipProofs = discovery.ownershipProofs;
|
|
1456
1472
|
}
|
|
1457
1473
|
if (instructions) {
|
|
1458
1474
|
body.instructions = instructions;
|
|
@@ -1475,7 +1491,7 @@ function toDiscoveryResource(method, url, mode) {
|
|
|
1475
1491
|
|
|
1476
1492
|
// src/discovery/openapi.ts
|
|
1477
1493
|
import { NextResponse as NextResponse4 } from "next/server";
|
|
1478
|
-
function createOpenAPIHandler(registry, baseUrl, pricesKeys,
|
|
1494
|
+
function createOpenAPIHandler(registry, baseUrl, pricesKeys, discovery) {
|
|
1479
1495
|
const normalizedBase = baseUrl.replace(/\/+$/, "");
|
|
1480
1496
|
let cached = null;
|
|
1481
1497
|
let validated = false;
|
|
@@ -1490,12 +1506,12 @@ function createOpenAPIHandler(registry, baseUrl, pricesKeys, options) {
|
|
|
1490
1506
|
const tagSet = /* @__PURE__ */ new Set();
|
|
1491
1507
|
let requiresSiwxScheme = false;
|
|
1492
1508
|
let requiresApiKeyScheme = false;
|
|
1493
|
-
for (const [
|
|
1494
|
-
const apiPath = `/api/${entry.path ?? key}`;
|
|
1509
|
+
for (const [, entry] of registry.entries()) {
|
|
1510
|
+
const apiPath = `/api/${entry.path ?? entry.key}`;
|
|
1495
1511
|
const method = entry.method.toLowerCase();
|
|
1496
|
-
const tag = deriveTag(key);
|
|
1512
|
+
const tag = deriveTag(entry.key);
|
|
1497
1513
|
tagSet.add(tag);
|
|
1498
|
-
const built = buildOperation(key, entry, tag);
|
|
1514
|
+
const built = buildOperation(entry.key, entry, tag);
|
|
1499
1515
|
if (built.requiresSiwxScheme) requiresSiwxScheme = true;
|
|
1500
1516
|
if (built.requiresApiKeyScheme) requiresApiKeyScheme = true;
|
|
1501
1517
|
paths[apiPath] = { ...paths[apiPath], [method]: built.operation };
|
|
@@ -1516,21 +1532,20 @@ function createOpenAPIHandler(registry, baseUrl, pricesKeys, options) {
|
|
|
1516
1532
|
};
|
|
1517
1533
|
}
|
|
1518
1534
|
const discoveryMetadata = {};
|
|
1519
|
-
if (
|
|
1520
|
-
discoveryMetadata.ownershipProofs =
|
|
1521
|
-
}
|
|
1522
|
-
if (options.llmsTxtUrl) {
|
|
1523
|
-
discoveryMetadata.llmsTxtUrl = options.llmsTxtUrl;
|
|
1535
|
+
if (discovery.ownershipProofs && discovery.ownershipProofs.length > 0) {
|
|
1536
|
+
discoveryMetadata.ownershipProofs = discovery.ownershipProofs;
|
|
1524
1537
|
}
|
|
1538
|
+
const guidance = await resolveGuidance(discovery);
|
|
1525
1539
|
const openApiDocument = {
|
|
1526
1540
|
openapi: "3.1.0",
|
|
1527
1541
|
info: {
|
|
1528
|
-
title:
|
|
1529
|
-
description:
|
|
1530
|
-
version:
|
|
1531
|
-
|
|
1542
|
+
title: discovery.title,
|
|
1543
|
+
description: discovery.description,
|
|
1544
|
+
version: discovery.version,
|
|
1545
|
+
guidance,
|
|
1546
|
+
...discovery.contact && { contact: discovery.contact }
|
|
1532
1547
|
},
|
|
1533
|
-
servers: [{ url: (
|
|
1548
|
+
servers: [{ url: (discovery.serverUrl ?? normalizedBase).replace(/\/+$/, "") }],
|
|
1534
1549
|
tags: Array.from(tagSet).sort().map((name) => ({ name })),
|
|
1535
1550
|
...Object.keys(securitySchemes).length > 0 ? {
|
|
1536
1551
|
components: {
|
|
@@ -1650,6 +1665,20 @@ function buildPricingInfo(entry) {
|
|
|
1650
1665
|
return void 0;
|
|
1651
1666
|
}
|
|
1652
1667
|
|
|
1668
|
+
// src/discovery/llms-txt.ts
|
|
1669
|
+
import { NextResponse as NextResponse5 } from "next/server";
|
|
1670
|
+
function createLlmsTxtHandler(discovery) {
|
|
1671
|
+
return async (_request) => {
|
|
1672
|
+
const guidance = await resolveGuidance(discovery) ?? "";
|
|
1673
|
+
return new NextResponse5(guidance, {
|
|
1674
|
+
headers: {
|
|
1675
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
1676
|
+
"Access-Control-Allow-Origin": "*"
|
|
1677
|
+
}
|
|
1678
|
+
});
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1653
1682
|
// src/index.ts
|
|
1654
1683
|
function createRouter(config) {
|
|
1655
1684
|
const registry = new RouteRegistry();
|
|
@@ -1778,11 +1807,14 @@ function createRouter(config) {
|
|
|
1778
1807
|
}
|
|
1779
1808
|
return builder;
|
|
1780
1809
|
},
|
|
1781
|
-
wellKnown(
|
|
1782
|
-
return createWellKnownHandler(registry, resolvedBaseUrl, pricesKeys,
|
|
1810
|
+
wellKnown() {
|
|
1811
|
+
return createWellKnownHandler(registry, resolvedBaseUrl, pricesKeys, config.discovery);
|
|
1812
|
+
},
|
|
1813
|
+
openapi() {
|
|
1814
|
+
return createOpenAPIHandler(registry, resolvedBaseUrl, pricesKeys, config.discovery);
|
|
1783
1815
|
},
|
|
1784
|
-
|
|
1785
|
-
return
|
|
1816
|
+
llmsTxt() {
|
|
1817
|
+
return createLlmsTxtHandler(config.discovery);
|
|
1786
1818
|
},
|
|
1787
1819
|
monitors() {
|
|
1788
1820
|
const result = [];
|