@agentcash/router 1.3.3 → 1.4.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 +136 -2
- package/dist/index.d.cts +114 -16
- package/dist/index.d.ts +114 -16
- package/dist/index.js +136 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1153,6 +1153,9 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1153
1153
|
return fail(status, message, meta, pluginCtx, earlyBodyData);
|
|
1154
1154
|
}
|
|
1155
1155
|
}
|
|
1156
|
+
} else {
|
|
1157
|
+
firePluginResponse(deps, pluginCtx, meta, earlyBodyResult.response);
|
|
1158
|
+
return earlyBodyResult.response;
|
|
1156
1159
|
}
|
|
1157
1160
|
}
|
|
1158
1161
|
if (routeEntry.authMode === "siwx" || routeEntry.siwxEnabled) {
|
|
@@ -1769,10 +1772,16 @@ async function build402(request, routeEntry, deps, meta, pluginCtx, bodyData) {
|
|
|
1769
1772
|
const outputSchema = routeEntry.outputSchema ? toJSON(routeEntry.outputSchema) : void 0;
|
|
1770
1773
|
if (inputSchema) {
|
|
1771
1774
|
const config = {
|
|
1775
|
+
method: routeEntry.method,
|
|
1772
1776
|
bodyType: routeEntry.bodySchema ? "json" : void 0,
|
|
1773
1777
|
inputSchema
|
|
1774
1778
|
};
|
|
1775
|
-
if (
|
|
1779
|
+
if (routeEntry.inputExample !== void 0) {
|
|
1780
|
+
config.input = routeEntry.inputExample;
|
|
1781
|
+
}
|
|
1782
|
+
if (outputSchema && routeEntry.outputExample !== void 0) {
|
|
1783
|
+
config.output = { schema: outputSchema, example: routeEntry.outputExample };
|
|
1784
|
+
}
|
|
1776
1785
|
extensions = declareDiscoveryExtension(config);
|
|
1777
1786
|
}
|
|
1778
1787
|
} catch (err) {
|
|
@@ -1891,6 +1900,46 @@ function fireProviderQuota(routeEntry, response, handlerResult, deps, pluginCtx)
|
|
|
1891
1900
|
}
|
|
1892
1901
|
}
|
|
1893
1902
|
|
|
1903
|
+
// src/validate-examples.ts
|
|
1904
|
+
function validateExamples(key, bodySchema, querySchema, outputSchema, inputExample, hasInputExample, outputExample, hasOutputExample) {
|
|
1905
|
+
if (bodySchema && !hasInputExample) {
|
|
1906
|
+
throw new Error(
|
|
1907
|
+
`route '${key}': .body() requires a matching .inputExample() \u2014 the bazaar discovery extension needs a conforming sample body to advertise.`
|
|
1908
|
+
);
|
|
1909
|
+
}
|
|
1910
|
+
if (querySchema && !hasInputExample) {
|
|
1911
|
+
throw new Error(
|
|
1912
|
+
`route '${key}': .query() requires a matching .inputExample() \u2014 the bazaar discovery extension needs a conforming sample query to advertise.`
|
|
1913
|
+
);
|
|
1914
|
+
}
|
|
1915
|
+
if (outputSchema && !hasOutputExample) {
|
|
1916
|
+
throw new Error(
|
|
1917
|
+
`route '${key}': .output() requires a matching .outputExample() \u2014 the bazaar discovery extension needs a conforming sample response to advertise.`
|
|
1918
|
+
);
|
|
1919
|
+
}
|
|
1920
|
+
const inputSchema = bodySchema ?? querySchema;
|
|
1921
|
+
if (inputSchema && hasInputExample) {
|
|
1922
|
+
const result = inputSchema.safeParse(inputExample);
|
|
1923
|
+
if (!result.success) {
|
|
1924
|
+
const issues = result.error.issues.map((i) => ` \u2022 ${i.path.length ? i.path.join(".") : "<root>"}: ${i.message}`).join("\n");
|
|
1925
|
+
throw new Error(
|
|
1926
|
+
`route '${key}': .inputExample() does not satisfy ${bodySchema ? ".body()" : ".query()"} schema:
|
|
1927
|
+
${issues}`
|
|
1928
|
+
);
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
if (outputSchema && hasOutputExample) {
|
|
1932
|
+
const result = outputSchema.safeParse(outputExample);
|
|
1933
|
+
if (!result.success) {
|
|
1934
|
+
const issues = result.error.issues.map((i) => ` \u2022 ${i.path.length ? i.path.join(".") : "<root>"}: ${i.message}`).join("\n");
|
|
1935
|
+
throw new Error(
|
|
1936
|
+
`route '${key}': .outputExample() does not satisfy .output() schema:
|
|
1937
|
+
${issues}`
|
|
1938
|
+
);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1894
1943
|
// src/builder.ts
|
|
1895
1944
|
var RouteBuilder = class {
|
|
1896
1945
|
/** @internal */
|
|
@@ -1920,6 +1969,14 @@ var RouteBuilder = class {
|
|
|
1920
1969
|
/** @internal */
|
|
1921
1970
|
_outputSchema;
|
|
1922
1971
|
/** @internal */
|
|
1972
|
+
_inputExample = void 0;
|
|
1973
|
+
/** @internal */
|
|
1974
|
+
_hasInputExample = false;
|
|
1975
|
+
/** @internal */
|
|
1976
|
+
_outputExample = void 0;
|
|
1977
|
+
/** @internal */
|
|
1978
|
+
_hasOutputExample = false;
|
|
1979
|
+
/** @internal */
|
|
1923
1980
|
_description;
|
|
1924
1981
|
/** @internal */
|
|
1925
1982
|
_path;
|
|
@@ -2049,6 +2106,63 @@ var RouteBuilder = class {
|
|
|
2049
2106
|
next._outputSchema = schema;
|
|
2050
2107
|
return next;
|
|
2051
2108
|
}
|
|
2109
|
+
/**
|
|
2110
|
+
* Provide a conforming example of the request input (body or query params).
|
|
2111
|
+
*
|
|
2112
|
+
* **Required** whenever `.body()` or `.query()` is set — enforced at compile time via
|
|
2113
|
+
* `.handler()` overloads, and at route-registration time via Zod validation of the
|
|
2114
|
+
* example against the schema. The example is embedded in the bazaar discovery extension
|
|
2115
|
+
* so indexers can advertise a working sample call.
|
|
2116
|
+
*
|
|
2117
|
+
* @example
|
|
2118
|
+
* ```ts
|
|
2119
|
+
* router.route('search')
|
|
2120
|
+
* .paid('0.01')
|
|
2121
|
+
* .body(z.object({ q: z.string() }))
|
|
2122
|
+
* .inputExample({ q: 'hello world' })
|
|
2123
|
+
* .handler(async ({ body }) => { ... });
|
|
2124
|
+
* ```
|
|
2125
|
+
*/
|
|
2126
|
+
inputExample(example) {
|
|
2127
|
+
const next = this.fork();
|
|
2128
|
+
next._inputExample = example;
|
|
2129
|
+
next._hasInputExample = true;
|
|
2130
|
+
return next;
|
|
2131
|
+
}
|
|
2132
|
+
/**
|
|
2133
|
+
* Provide a conforming example of the response output.
|
|
2134
|
+
*
|
|
2135
|
+
* **Required** whenever `.output()` is set — enforced at compile time via `.handler()`
|
|
2136
|
+
* overloads, and at route-registration time via Zod validation of the example against
|
|
2137
|
+
* the schema. The example is embedded in the bazaar discovery extension so indexers
|
|
2138
|
+
* can advertise the response shape.
|
|
2139
|
+
*
|
|
2140
|
+
* Accepts any JSON value (objects, arrays, or primitives) — top-level array
|
|
2141
|
+
* or primitive responses (e.g. `z.array(...)`) are supported alongside the
|
|
2142
|
+
* common object case.
|
|
2143
|
+
*
|
|
2144
|
+
* @example
|
|
2145
|
+
* ```ts
|
|
2146
|
+
* router.route('search')
|
|
2147
|
+
* .paid('0.01')
|
|
2148
|
+
* .output(z.object({ results: z.array(z.string()) }))
|
|
2149
|
+
* .outputExample({ results: ['a', 'b'] })
|
|
2150
|
+
* .handler(async () => { ... });
|
|
2151
|
+
*
|
|
2152
|
+
* // Top-level array response
|
|
2153
|
+
* router.route('chains')
|
|
2154
|
+
* .paid('0.01')
|
|
2155
|
+
* .output(z.array(z.object({ name: z.string() })))
|
|
2156
|
+
* .outputExample([{ name: 'Ethereum' }])
|
|
2157
|
+
* .handler(async () => { ... });
|
|
2158
|
+
* ```
|
|
2159
|
+
*/
|
|
2160
|
+
outputExample(example) {
|
|
2161
|
+
const next = this.fork();
|
|
2162
|
+
next._outputExample = example;
|
|
2163
|
+
next._hasOutputExample = true;
|
|
2164
|
+
return next;
|
|
2165
|
+
}
|
|
2052
2166
|
description(text) {
|
|
2053
2167
|
const next = this.fork();
|
|
2054
2168
|
next._description = text;
|
|
@@ -2093,12 +2207,26 @@ var RouteBuilder = class {
|
|
|
2093
2207
|
next._validateFn = fn;
|
|
2094
2208
|
return next;
|
|
2095
2209
|
}
|
|
2210
|
+
// -------------------------------------------------------------------------
|
|
2211
|
+
// Terminal method
|
|
2212
|
+
// -------------------------------------------------------------------------
|
|
2096
2213
|
handler(fn) {
|
|
2214
|
+
const handlerFn = fn;
|
|
2097
2215
|
if (this._validateFn && !this._bodySchema) {
|
|
2098
2216
|
throw new Error(
|
|
2099
2217
|
`route '${this._key}': .validate() requires .body() \u2014 validation runs on parsed body`
|
|
2100
2218
|
);
|
|
2101
2219
|
}
|
|
2220
|
+
validateExamples(
|
|
2221
|
+
this._key,
|
|
2222
|
+
this._bodySchema,
|
|
2223
|
+
this._querySchema,
|
|
2224
|
+
this._outputSchema,
|
|
2225
|
+
this._inputExample,
|
|
2226
|
+
this._hasInputExample,
|
|
2227
|
+
this._outputExample,
|
|
2228
|
+
this._hasOutputExample
|
|
2229
|
+
);
|
|
2102
2230
|
const entry = {
|
|
2103
2231
|
key: this._key,
|
|
2104
2232
|
authMode: this._authMode,
|
|
@@ -2108,6 +2236,8 @@ var RouteBuilder = class {
|
|
|
2108
2236
|
bodySchema: this._bodySchema,
|
|
2109
2237
|
querySchema: this._querySchema,
|
|
2110
2238
|
outputSchema: this._outputSchema,
|
|
2239
|
+
inputExample: this._hasInputExample ? this._inputExample : void 0,
|
|
2240
|
+
outputExample: this._hasOutputExample ? this._outputExample : void 0,
|
|
2111
2241
|
description: this._description,
|
|
2112
2242
|
path: this._path,
|
|
2113
2243
|
method: this._method,
|
|
@@ -2121,7 +2251,11 @@ var RouteBuilder = class {
|
|
|
2121
2251
|
mppInfo: this._mppInfo
|
|
2122
2252
|
};
|
|
2123
2253
|
this._registry.register(entry);
|
|
2124
|
-
return createRequestHandler(
|
|
2254
|
+
return createRequestHandler(
|
|
2255
|
+
entry,
|
|
2256
|
+
handlerFn,
|
|
2257
|
+
this._deps
|
|
2258
|
+
);
|
|
2125
2259
|
}
|
|
2126
2260
|
};
|
|
2127
2261
|
|
package/dist/index.d.cts
CHANGED
|
@@ -163,6 +163,11 @@ interface AlertEvent {
|
|
|
163
163
|
meta?: Record<string, unknown>;
|
|
164
164
|
}
|
|
165
165
|
type AlertFn = (level: AlertLevel, message: string, meta?: Record<string, unknown>) => void;
|
|
166
|
+
type JsonPrimitive = string | number | boolean | null;
|
|
167
|
+
type JsonValue = JsonPrimitive | JsonObject | JsonValue[];
|
|
168
|
+
type JsonObject = {
|
|
169
|
+
[key: string]: JsonValue;
|
|
170
|
+
};
|
|
166
171
|
|
|
167
172
|
interface X402Server {
|
|
168
173
|
initialize(): Promise<void>;
|
|
@@ -303,6 +308,25 @@ interface RouteEntry {
|
|
|
303
308
|
bodySchema?: ZodType;
|
|
304
309
|
querySchema?: ZodType;
|
|
305
310
|
outputSchema?: ZodType;
|
|
311
|
+
/**
|
|
312
|
+
* Conforming example for the request input (body for body routes, query params for query routes).
|
|
313
|
+
* Required whenever `bodySchema` or `querySchema` is set. Must satisfy the corresponding schema —
|
|
314
|
+
* validated at route-registration time via the Zod schema.
|
|
315
|
+
*
|
|
316
|
+
* Emitted in the bazaar discovery extension so indexers can advertise a working sample call.
|
|
317
|
+
*/
|
|
318
|
+
inputExample?: JsonObject;
|
|
319
|
+
/**
|
|
320
|
+
* Conforming example for the response output. Required whenever `outputSchema` is set.
|
|
321
|
+
* Must satisfy `outputSchema` — validated at route-registration time via the Zod schema.
|
|
322
|
+
*
|
|
323
|
+
* Accepts any JSON value (object, array, or primitive) to support top-level array or
|
|
324
|
+
* primitive response schemas.
|
|
325
|
+
*
|
|
326
|
+
* Emitted in the bazaar discovery extension. Without it the `output` block is dropped from
|
|
327
|
+
* the declaration entirely (the output schema alone cannot be exposed in bazaar without an example).
|
|
328
|
+
*/
|
|
329
|
+
outputExample?: JsonValue;
|
|
306
330
|
description?: string;
|
|
307
331
|
path?: string;
|
|
308
332
|
method: RouteMethod;
|
|
@@ -468,7 +492,33 @@ interface OrchestrateDeps {
|
|
|
468
492
|
|
|
469
493
|
type True = true;
|
|
470
494
|
type False = false;
|
|
471
|
-
|
|
495
|
+
/**
|
|
496
|
+
* Active request-input type at a builder position. Resolves to `TBody` when
|
|
497
|
+
* `.body()` has been called, `TQuery` when `.query()` has been called, and
|
|
498
|
+
* `never` when neither — making `.inputExample()` unusable before a schema
|
|
499
|
+
* is set (the literal won't assign to `never`).
|
|
500
|
+
*/
|
|
501
|
+
type InputTypeFor<TBody, TQuery> = [TBody] extends [undefined] ? [TQuery] extends [undefined] ? never : TQuery : TBody;
|
|
502
|
+
/**
|
|
503
|
+
* The handler argument type. Narrows to the real handler signature when the
|
|
504
|
+
* builder state is valid, and to a descriptive error object when it isn't —
|
|
505
|
+
* the mismatch surfaces as a TS type error at the `.handler(...)` call site
|
|
506
|
+
* with the `__missing` string as the contextual hint.
|
|
507
|
+
*
|
|
508
|
+
* Encoded as a conditional argument rather than overload `this:` constraints
|
|
509
|
+
* because TypeScript doesn't reliably gate overload selection on `this` for
|
|
510
|
+
* generic classes (structurally identical instance types collapse).
|
|
511
|
+
*/
|
|
512
|
+
type HandlerArg<TBody, TQuery, HasAuth extends boolean, NeedsBody extends boolean, HasBody extends boolean, NeedsInputExample extends boolean, NeedsOutputExample extends boolean> = HasAuth extends true ? [NeedsBody, HasBody] extends [true, false] ? {
|
|
513
|
+
__missing: 'Call .body(schema) — dynamic/tiered pricing requires a body schema to resolve the price against';
|
|
514
|
+
} : NeedsInputExample extends true ? {
|
|
515
|
+
__missing: 'Call .inputExample(sample) — .body()/.query() routes must advertise a conforming request example for bazaar discovery';
|
|
516
|
+
} : NeedsOutputExample extends true ? {
|
|
517
|
+
__missing: 'Call .outputExample(sample) — .output() routes must advertise a conforming response example for bazaar discovery';
|
|
518
|
+
} : (ctx: HandlerContext<TBody, TQuery>) => Promise<unknown> : {
|
|
519
|
+
__missing: 'Select an auth mode: .paid(pricing), .siwx(), .apiKey(resolver), or .unprotected()';
|
|
520
|
+
};
|
|
521
|
+
declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = undefined, HasAuth extends boolean = false, NeedsBody extends boolean = false, HasBody extends boolean = false, NeedsInputExample extends boolean = false, NeedsOutputExample extends boolean = false> {
|
|
472
522
|
/** @internal */ readonly _key: string;
|
|
473
523
|
/** @internal */ readonly _registry: RouteRegistry;
|
|
474
524
|
/** @internal */ readonly _deps: OrchestrateDeps;
|
|
@@ -482,6 +532,10 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, HasAuth extend
|
|
|
482
532
|
/** @internal */ _bodySchema: ZodType | undefined;
|
|
483
533
|
/** @internal */ _querySchema: ZodType | undefined;
|
|
484
534
|
/** @internal */ _outputSchema: ZodType | undefined;
|
|
535
|
+
/** @internal */ _inputExample: JsonObject | undefined;
|
|
536
|
+
/** @internal */ _hasInputExample: boolean;
|
|
537
|
+
/** @internal */ _outputExample: JsonValue | undefined;
|
|
538
|
+
/** @internal */ _hasOutputExample: boolean;
|
|
485
539
|
/** @internal */ _description: string | undefined;
|
|
486
540
|
/** @internal */ _path: string | undefined;
|
|
487
541
|
/** @internal */ _method: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
|
|
@@ -492,10 +546,10 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, HasAuth extend
|
|
|
492
546
|
/** @internal */ _mppInfo: MppProtocolInfo | undefined;
|
|
493
547
|
constructor(key: string, registry: RouteRegistry, deps: OrchestrateDeps);
|
|
494
548
|
private fork;
|
|
495
|
-
paid(pricing: string, options?: PaidOptions): RouteBuilder<TBody, TQuery, True, False, HasBody>;
|
|
549
|
+
paid(pricing: string, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
496
550
|
paid<TBodyIn>(pricing: (body: TBodyIn) => string | Promise<string>, options?: PaidOptions & {
|
|
497
551
|
maxPrice?: string;
|
|
498
|
-
}): RouteBuilder<TBody, TQuery, True, True, HasBody>;
|
|
552
|
+
}): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
499
553
|
paid(pricing: {
|
|
500
554
|
field: string;
|
|
501
555
|
tiers: Record<string, {
|
|
@@ -503,14 +557,61 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, HasAuth extend
|
|
|
503
557
|
label?: string;
|
|
504
558
|
}>;
|
|
505
559
|
default?: string;
|
|
506
|
-
}, options?: PaidOptions): RouteBuilder<TBody, TQuery, True, True, HasBody>;
|
|
507
|
-
siwx(): RouteBuilder<TBody, TQuery, True, False, HasBody>;
|
|
508
|
-
apiKey(resolver: (key: string) => unknown | Promise<unknown>): RouteBuilder<TBody, TQuery, True, NeedsBody, HasBody>;
|
|
509
|
-
unprotected(): RouteBuilder<TBody, TQuery, True, False, HasBody>;
|
|
560
|
+
}, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
561
|
+
siwx(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
562
|
+
apiKey(resolver: (key: string) => unknown | Promise<unknown>): RouteBuilder<TBody, TQuery, TOutput, True, NeedsBody, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
563
|
+
unprotected(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
510
564
|
provider(name: string, config?: ProviderConfig): this;
|
|
511
|
-
body<T>(schema: ZodType<T>): RouteBuilder<T, TQuery, HasAuth, NeedsBody, True>;
|
|
512
|
-
query<T>(schema: ZodType<T>): RouteBuilder<TBody, T, HasAuth, NeedsBody, HasBody>;
|
|
513
|
-
output(schema: ZodType):
|
|
565
|
+
body<T>(schema: ZodType<T>): RouteBuilder<T, TQuery, TOutput, HasAuth, NeedsBody, True, True, NeedsOutputExample>;
|
|
566
|
+
query<T>(schema: ZodType<T>): RouteBuilder<TBody, T, TOutput, HasAuth, NeedsBody, HasBody, True, NeedsOutputExample>;
|
|
567
|
+
output<T>(schema: ZodType<T>): RouteBuilder<TBody, TQuery, T, HasAuth, NeedsBody, HasBody, NeedsInputExample, True>;
|
|
568
|
+
/**
|
|
569
|
+
* Provide a conforming example of the request input (body or query params).
|
|
570
|
+
*
|
|
571
|
+
* **Required** whenever `.body()` or `.query()` is set — enforced at compile time via
|
|
572
|
+
* `.handler()` overloads, and at route-registration time via Zod validation of the
|
|
573
|
+
* example against the schema. The example is embedded in the bazaar discovery extension
|
|
574
|
+
* so indexers can advertise a working sample call.
|
|
575
|
+
*
|
|
576
|
+
* @example
|
|
577
|
+
* ```ts
|
|
578
|
+
* router.route('search')
|
|
579
|
+
* .paid('0.01')
|
|
580
|
+
* .body(z.object({ q: z.string() }))
|
|
581
|
+
* .inputExample({ q: 'hello world' })
|
|
582
|
+
* .handler(async ({ body }) => { ... });
|
|
583
|
+
* ```
|
|
584
|
+
*/
|
|
585
|
+
inputExample(example: InputTypeFor<TBody, TQuery> & JsonObject): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, False, NeedsOutputExample>;
|
|
586
|
+
/**
|
|
587
|
+
* Provide a conforming example of the response output.
|
|
588
|
+
*
|
|
589
|
+
* **Required** whenever `.output()` is set — enforced at compile time via `.handler()`
|
|
590
|
+
* overloads, and at route-registration time via Zod validation of the example against
|
|
591
|
+
* the schema. The example is embedded in the bazaar discovery extension so indexers
|
|
592
|
+
* can advertise the response shape.
|
|
593
|
+
*
|
|
594
|
+
* Accepts any JSON value (objects, arrays, or primitives) — top-level array
|
|
595
|
+
* or primitive responses (e.g. `z.array(...)`) are supported alongside the
|
|
596
|
+
* common object case.
|
|
597
|
+
*
|
|
598
|
+
* @example
|
|
599
|
+
* ```ts
|
|
600
|
+
* router.route('search')
|
|
601
|
+
* .paid('0.01')
|
|
602
|
+
* .output(z.object({ results: z.array(z.string()) }))
|
|
603
|
+
* .outputExample({ results: ['a', 'b'] })
|
|
604
|
+
* .handler(async () => { ... });
|
|
605
|
+
*
|
|
606
|
+
* // Top-level array response
|
|
607
|
+
* router.route('chains')
|
|
608
|
+
* .paid('0.01')
|
|
609
|
+
* .output(z.array(z.object({ name: z.string() })))
|
|
610
|
+
* .outputExample([{ name: 'Ethereum' }])
|
|
611
|
+
* .handler(async () => { ... });
|
|
612
|
+
* ```
|
|
613
|
+
*/
|
|
614
|
+
outputExample(example: TOutput & JsonValue): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, NeedsInputExample, False>;
|
|
514
615
|
description(text: string): this;
|
|
515
616
|
path(p: string): this;
|
|
516
617
|
method(m: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH'): this;
|
|
@@ -535,11 +636,8 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, HasAuth extend
|
|
|
535
636
|
* .handler(async ({ body }) => { ... });
|
|
536
637
|
* ```
|
|
537
638
|
*/
|
|
538
|
-
validate(fn: (body: TBody) => void | Promise<void>): RouteBuilder<TBody, TQuery, HasAuth, NeedsBody, HasBody>;
|
|
539
|
-
handler(
|
|
540
|
-
handler(this: RouteBuilder<TBody, TQuery, false, boolean, boolean>, fn: never): never;
|
|
541
|
-
handler(this: RouteBuilder<TBody, TQuery, True, False, HasBody>, fn: (ctx: HandlerContext<TBody, TQuery>) => Promise<unknown>): (request: NextRequest) => Promise<Response>;
|
|
542
|
-
handler(this: RouteBuilder<TBody, TQuery, True, True, True>, fn: (ctx: HandlerContext<TBody, TQuery>) => Promise<unknown>): (request: NextRequest) => Promise<Response>;
|
|
639
|
+
validate(fn: (body: TBody) => void | Promise<void>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
640
|
+
handler(fn: HandlerArg<TBody, TQuery, HasAuth, NeedsBody, HasBody, NeedsInputExample, NeedsOutputExample>): (request: NextRequest) => Promise<Response>;
|
|
543
641
|
}
|
|
544
642
|
|
|
545
643
|
interface MonitorEntry {
|
|
@@ -551,7 +649,7 @@ interface MonitorEntry {
|
|
|
551
649
|
critical?: number;
|
|
552
650
|
}
|
|
553
651
|
interface ServiceRouter<TPriceKeys extends string = never> {
|
|
554
|
-
route<K extends string>(keyOrDefinition: K | RouteDefinition<K>): [K] extends [TPriceKeys] ? RouteBuilder<undefined, undefined, true, false, false> : RouteBuilder<undefined, undefined, false, false, false>;
|
|
652
|
+
route<K extends string>(keyOrDefinition: K | RouteDefinition<K>): [K] extends [TPriceKeys] ? RouteBuilder<undefined, undefined, undefined, true, false, false, false, false> : RouteBuilder<undefined, undefined, undefined, false, false, false, false, false>;
|
|
555
653
|
wellKnown(): (request: NextRequest) => Promise<NextResponse>;
|
|
556
654
|
openapi(): (request: NextRequest) => Promise<NextResponse>;
|
|
557
655
|
llmsTxt(): (request: NextRequest) => Promise<NextResponse>;
|
package/dist/index.d.ts
CHANGED
|
@@ -163,6 +163,11 @@ interface AlertEvent {
|
|
|
163
163
|
meta?: Record<string, unknown>;
|
|
164
164
|
}
|
|
165
165
|
type AlertFn = (level: AlertLevel, message: string, meta?: Record<string, unknown>) => void;
|
|
166
|
+
type JsonPrimitive = string | number | boolean | null;
|
|
167
|
+
type JsonValue = JsonPrimitive | JsonObject | JsonValue[];
|
|
168
|
+
type JsonObject = {
|
|
169
|
+
[key: string]: JsonValue;
|
|
170
|
+
};
|
|
166
171
|
|
|
167
172
|
interface X402Server {
|
|
168
173
|
initialize(): Promise<void>;
|
|
@@ -303,6 +308,25 @@ interface RouteEntry {
|
|
|
303
308
|
bodySchema?: ZodType;
|
|
304
309
|
querySchema?: ZodType;
|
|
305
310
|
outputSchema?: ZodType;
|
|
311
|
+
/**
|
|
312
|
+
* Conforming example for the request input (body for body routes, query params for query routes).
|
|
313
|
+
* Required whenever `bodySchema` or `querySchema` is set. Must satisfy the corresponding schema —
|
|
314
|
+
* validated at route-registration time via the Zod schema.
|
|
315
|
+
*
|
|
316
|
+
* Emitted in the bazaar discovery extension so indexers can advertise a working sample call.
|
|
317
|
+
*/
|
|
318
|
+
inputExample?: JsonObject;
|
|
319
|
+
/**
|
|
320
|
+
* Conforming example for the response output. Required whenever `outputSchema` is set.
|
|
321
|
+
* Must satisfy `outputSchema` — validated at route-registration time via the Zod schema.
|
|
322
|
+
*
|
|
323
|
+
* Accepts any JSON value (object, array, or primitive) to support top-level array or
|
|
324
|
+
* primitive response schemas.
|
|
325
|
+
*
|
|
326
|
+
* Emitted in the bazaar discovery extension. Without it the `output` block is dropped from
|
|
327
|
+
* the declaration entirely (the output schema alone cannot be exposed in bazaar without an example).
|
|
328
|
+
*/
|
|
329
|
+
outputExample?: JsonValue;
|
|
306
330
|
description?: string;
|
|
307
331
|
path?: string;
|
|
308
332
|
method: RouteMethod;
|
|
@@ -468,7 +492,33 @@ interface OrchestrateDeps {
|
|
|
468
492
|
|
|
469
493
|
type True = true;
|
|
470
494
|
type False = false;
|
|
471
|
-
|
|
495
|
+
/**
|
|
496
|
+
* Active request-input type at a builder position. Resolves to `TBody` when
|
|
497
|
+
* `.body()` has been called, `TQuery` when `.query()` has been called, and
|
|
498
|
+
* `never` when neither — making `.inputExample()` unusable before a schema
|
|
499
|
+
* is set (the literal won't assign to `never`).
|
|
500
|
+
*/
|
|
501
|
+
type InputTypeFor<TBody, TQuery> = [TBody] extends [undefined] ? [TQuery] extends [undefined] ? never : TQuery : TBody;
|
|
502
|
+
/**
|
|
503
|
+
* The handler argument type. Narrows to the real handler signature when the
|
|
504
|
+
* builder state is valid, and to a descriptive error object when it isn't —
|
|
505
|
+
* the mismatch surfaces as a TS type error at the `.handler(...)` call site
|
|
506
|
+
* with the `__missing` string as the contextual hint.
|
|
507
|
+
*
|
|
508
|
+
* Encoded as a conditional argument rather than overload `this:` constraints
|
|
509
|
+
* because TypeScript doesn't reliably gate overload selection on `this` for
|
|
510
|
+
* generic classes (structurally identical instance types collapse).
|
|
511
|
+
*/
|
|
512
|
+
type HandlerArg<TBody, TQuery, HasAuth extends boolean, NeedsBody extends boolean, HasBody extends boolean, NeedsInputExample extends boolean, NeedsOutputExample extends boolean> = HasAuth extends true ? [NeedsBody, HasBody] extends [true, false] ? {
|
|
513
|
+
__missing: 'Call .body(schema) — dynamic/tiered pricing requires a body schema to resolve the price against';
|
|
514
|
+
} : NeedsInputExample extends true ? {
|
|
515
|
+
__missing: 'Call .inputExample(sample) — .body()/.query() routes must advertise a conforming request example for bazaar discovery';
|
|
516
|
+
} : NeedsOutputExample extends true ? {
|
|
517
|
+
__missing: 'Call .outputExample(sample) — .output() routes must advertise a conforming response example for bazaar discovery';
|
|
518
|
+
} : (ctx: HandlerContext<TBody, TQuery>) => Promise<unknown> : {
|
|
519
|
+
__missing: 'Select an auth mode: .paid(pricing), .siwx(), .apiKey(resolver), or .unprotected()';
|
|
520
|
+
};
|
|
521
|
+
declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = undefined, HasAuth extends boolean = false, NeedsBody extends boolean = false, HasBody extends boolean = false, NeedsInputExample extends boolean = false, NeedsOutputExample extends boolean = false> {
|
|
472
522
|
/** @internal */ readonly _key: string;
|
|
473
523
|
/** @internal */ readonly _registry: RouteRegistry;
|
|
474
524
|
/** @internal */ readonly _deps: OrchestrateDeps;
|
|
@@ -482,6 +532,10 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, HasAuth extend
|
|
|
482
532
|
/** @internal */ _bodySchema: ZodType | undefined;
|
|
483
533
|
/** @internal */ _querySchema: ZodType | undefined;
|
|
484
534
|
/** @internal */ _outputSchema: ZodType | undefined;
|
|
535
|
+
/** @internal */ _inputExample: JsonObject | undefined;
|
|
536
|
+
/** @internal */ _hasInputExample: boolean;
|
|
537
|
+
/** @internal */ _outputExample: JsonValue | undefined;
|
|
538
|
+
/** @internal */ _hasOutputExample: boolean;
|
|
485
539
|
/** @internal */ _description: string | undefined;
|
|
486
540
|
/** @internal */ _path: string | undefined;
|
|
487
541
|
/** @internal */ _method: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
|
|
@@ -492,10 +546,10 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, HasAuth extend
|
|
|
492
546
|
/** @internal */ _mppInfo: MppProtocolInfo | undefined;
|
|
493
547
|
constructor(key: string, registry: RouteRegistry, deps: OrchestrateDeps);
|
|
494
548
|
private fork;
|
|
495
|
-
paid(pricing: string, options?: PaidOptions): RouteBuilder<TBody, TQuery, True, False, HasBody>;
|
|
549
|
+
paid(pricing: string, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
496
550
|
paid<TBodyIn>(pricing: (body: TBodyIn) => string | Promise<string>, options?: PaidOptions & {
|
|
497
551
|
maxPrice?: string;
|
|
498
|
-
}): RouteBuilder<TBody, TQuery, True, True, HasBody>;
|
|
552
|
+
}): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
499
553
|
paid(pricing: {
|
|
500
554
|
field: string;
|
|
501
555
|
tiers: Record<string, {
|
|
@@ -503,14 +557,61 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, HasAuth extend
|
|
|
503
557
|
label?: string;
|
|
504
558
|
}>;
|
|
505
559
|
default?: string;
|
|
506
|
-
}, options?: PaidOptions): RouteBuilder<TBody, TQuery, True, True, HasBody>;
|
|
507
|
-
siwx(): RouteBuilder<TBody, TQuery, True, False, HasBody>;
|
|
508
|
-
apiKey(resolver: (key: string) => unknown | Promise<unknown>): RouteBuilder<TBody, TQuery, True, NeedsBody, HasBody>;
|
|
509
|
-
unprotected(): RouteBuilder<TBody, TQuery, True, False, HasBody>;
|
|
560
|
+
}, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
561
|
+
siwx(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
562
|
+
apiKey(resolver: (key: string) => unknown | Promise<unknown>): RouteBuilder<TBody, TQuery, TOutput, True, NeedsBody, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
563
|
+
unprotected(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
510
564
|
provider(name: string, config?: ProviderConfig): this;
|
|
511
|
-
body<T>(schema: ZodType<T>): RouteBuilder<T, TQuery, HasAuth, NeedsBody, True>;
|
|
512
|
-
query<T>(schema: ZodType<T>): RouteBuilder<TBody, T, HasAuth, NeedsBody, HasBody>;
|
|
513
|
-
output(schema: ZodType):
|
|
565
|
+
body<T>(schema: ZodType<T>): RouteBuilder<T, TQuery, TOutput, HasAuth, NeedsBody, True, True, NeedsOutputExample>;
|
|
566
|
+
query<T>(schema: ZodType<T>): RouteBuilder<TBody, T, TOutput, HasAuth, NeedsBody, HasBody, True, NeedsOutputExample>;
|
|
567
|
+
output<T>(schema: ZodType<T>): RouteBuilder<TBody, TQuery, T, HasAuth, NeedsBody, HasBody, NeedsInputExample, True>;
|
|
568
|
+
/**
|
|
569
|
+
* Provide a conforming example of the request input (body or query params).
|
|
570
|
+
*
|
|
571
|
+
* **Required** whenever `.body()` or `.query()` is set — enforced at compile time via
|
|
572
|
+
* `.handler()` overloads, and at route-registration time via Zod validation of the
|
|
573
|
+
* example against the schema. The example is embedded in the bazaar discovery extension
|
|
574
|
+
* so indexers can advertise a working sample call.
|
|
575
|
+
*
|
|
576
|
+
* @example
|
|
577
|
+
* ```ts
|
|
578
|
+
* router.route('search')
|
|
579
|
+
* .paid('0.01')
|
|
580
|
+
* .body(z.object({ q: z.string() }))
|
|
581
|
+
* .inputExample({ q: 'hello world' })
|
|
582
|
+
* .handler(async ({ body }) => { ... });
|
|
583
|
+
* ```
|
|
584
|
+
*/
|
|
585
|
+
inputExample(example: InputTypeFor<TBody, TQuery> & JsonObject): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, False, NeedsOutputExample>;
|
|
586
|
+
/**
|
|
587
|
+
* Provide a conforming example of the response output.
|
|
588
|
+
*
|
|
589
|
+
* **Required** whenever `.output()` is set — enforced at compile time via `.handler()`
|
|
590
|
+
* overloads, and at route-registration time via Zod validation of the example against
|
|
591
|
+
* the schema. The example is embedded in the bazaar discovery extension so indexers
|
|
592
|
+
* can advertise the response shape.
|
|
593
|
+
*
|
|
594
|
+
* Accepts any JSON value (objects, arrays, or primitives) — top-level array
|
|
595
|
+
* or primitive responses (e.g. `z.array(...)`) are supported alongside the
|
|
596
|
+
* common object case.
|
|
597
|
+
*
|
|
598
|
+
* @example
|
|
599
|
+
* ```ts
|
|
600
|
+
* router.route('search')
|
|
601
|
+
* .paid('0.01')
|
|
602
|
+
* .output(z.object({ results: z.array(z.string()) }))
|
|
603
|
+
* .outputExample({ results: ['a', 'b'] })
|
|
604
|
+
* .handler(async () => { ... });
|
|
605
|
+
*
|
|
606
|
+
* // Top-level array response
|
|
607
|
+
* router.route('chains')
|
|
608
|
+
* .paid('0.01')
|
|
609
|
+
* .output(z.array(z.object({ name: z.string() })))
|
|
610
|
+
* .outputExample([{ name: 'Ethereum' }])
|
|
611
|
+
* .handler(async () => { ... });
|
|
612
|
+
* ```
|
|
613
|
+
*/
|
|
614
|
+
outputExample(example: TOutput & JsonValue): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, NeedsInputExample, False>;
|
|
514
615
|
description(text: string): this;
|
|
515
616
|
path(p: string): this;
|
|
516
617
|
method(m: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH'): this;
|
|
@@ -535,11 +636,8 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, HasAuth extend
|
|
|
535
636
|
* .handler(async ({ body }) => { ... });
|
|
536
637
|
* ```
|
|
537
638
|
*/
|
|
538
|
-
validate(fn: (body: TBody) => void | Promise<void>): RouteBuilder<TBody, TQuery, HasAuth, NeedsBody, HasBody>;
|
|
539
|
-
handler(
|
|
540
|
-
handler(this: RouteBuilder<TBody, TQuery, false, boolean, boolean>, fn: never): never;
|
|
541
|
-
handler(this: RouteBuilder<TBody, TQuery, True, False, HasBody>, fn: (ctx: HandlerContext<TBody, TQuery>) => Promise<unknown>): (request: NextRequest) => Promise<Response>;
|
|
542
|
-
handler(this: RouteBuilder<TBody, TQuery, True, True, True>, fn: (ctx: HandlerContext<TBody, TQuery>) => Promise<unknown>): (request: NextRequest) => Promise<Response>;
|
|
639
|
+
validate(fn: (body: TBody) => void | Promise<void>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, NeedsInputExample, NeedsOutputExample>;
|
|
640
|
+
handler(fn: HandlerArg<TBody, TQuery, HasAuth, NeedsBody, HasBody, NeedsInputExample, NeedsOutputExample>): (request: NextRequest) => Promise<Response>;
|
|
543
641
|
}
|
|
544
642
|
|
|
545
643
|
interface MonitorEntry {
|
|
@@ -551,7 +649,7 @@ interface MonitorEntry {
|
|
|
551
649
|
critical?: number;
|
|
552
650
|
}
|
|
553
651
|
interface ServiceRouter<TPriceKeys extends string = never> {
|
|
554
|
-
route<K extends string>(keyOrDefinition: K | RouteDefinition<K>): [K] extends [TPriceKeys] ? RouteBuilder<undefined, undefined, true, false, false> : RouteBuilder<undefined, undefined, false, false, false>;
|
|
652
|
+
route<K extends string>(keyOrDefinition: K | RouteDefinition<K>): [K] extends [TPriceKeys] ? RouteBuilder<undefined, undefined, undefined, true, false, false, false, false> : RouteBuilder<undefined, undefined, undefined, false, false, false, false, false>;
|
|
555
653
|
wellKnown(): (request: NextRequest) => Promise<NextResponse>;
|
|
556
654
|
openapi(): (request: NextRequest) => Promise<NextResponse>;
|
|
557
655
|
llmsTxt(): (request: NextRequest) => Promise<NextResponse>;
|
package/dist/index.js
CHANGED
|
@@ -1114,6 +1114,9 @@ function createRequestHandler(routeEntry, handler, deps) {
|
|
|
1114
1114
|
return fail(status, message, meta, pluginCtx, earlyBodyData);
|
|
1115
1115
|
}
|
|
1116
1116
|
}
|
|
1117
|
+
} else {
|
|
1118
|
+
firePluginResponse(deps, pluginCtx, meta, earlyBodyResult.response);
|
|
1119
|
+
return earlyBodyResult.response;
|
|
1117
1120
|
}
|
|
1118
1121
|
}
|
|
1119
1122
|
if (routeEntry.authMode === "siwx" || routeEntry.siwxEnabled) {
|
|
@@ -1730,10 +1733,16 @@ async function build402(request, routeEntry, deps, meta, pluginCtx, bodyData) {
|
|
|
1730
1733
|
const outputSchema = routeEntry.outputSchema ? toJSON(routeEntry.outputSchema) : void 0;
|
|
1731
1734
|
if (inputSchema) {
|
|
1732
1735
|
const config = {
|
|
1736
|
+
method: routeEntry.method,
|
|
1733
1737
|
bodyType: routeEntry.bodySchema ? "json" : void 0,
|
|
1734
1738
|
inputSchema
|
|
1735
1739
|
};
|
|
1736
|
-
if (
|
|
1740
|
+
if (routeEntry.inputExample !== void 0) {
|
|
1741
|
+
config.input = routeEntry.inputExample;
|
|
1742
|
+
}
|
|
1743
|
+
if (outputSchema && routeEntry.outputExample !== void 0) {
|
|
1744
|
+
config.output = { schema: outputSchema, example: routeEntry.outputExample };
|
|
1745
|
+
}
|
|
1737
1746
|
extensions = declareDiscoveryExtension(config);
|
|
1738
1747
|
}
|
|
1739
1748
|
} catch (err) {
|
|
@@ -1852,6 +1861,46 @@ function fireProviderQuota(routeEntry, response, handlerResult, deps, pluginCtx)
|
|
|
1852
1861
|
}
|
|
1853
1862
|
}
|
|
1854
1863
|
|
|
1864
|
+
// src/validate-examples.ts
|
|
1865
|
+
function validateExamples(key, bodySchema, querySchema, outputSchema, inputExample, hasInputExample, outputExample, hasOutputExample) {
|
|
1866
|
+
if (bodySchema && !hasInputExample) {
|
|
1867
|
+
throw new Error(
|
|
1868
|
+
`route '${key}': .body() requires a matching .inputExample() \u2014 the bazaar discovery extension needs a conforming sample body to advertise.`
|
|
1869
|
+
);
|
|
1870
|
+
}
|
|
1871
|
+
if (querySchema && !hasInputExample) {
|
|
1872
|
+
throw new Error(
|
|
1873
|
+
`route '${key}': .query() requires a matching .inputExample() \u2014 the bazaar discovery extension needs a conforming sample query to advertise.`
|
|
1874
|
+
);
|
|
1875
|
+
}
|
|
1876
|
+
if (outputSchema && !hasOutputExample) {
|
|
1877
|
+
throw new Error(
|
|
1878
|
+
`route '${key}': .output() requires a matching .outputExample() \u2014 the bazaar discovery extension needs a conforming sample response to advertise.`
|
|
1879
|
+
);
|
|
1880
|
+
}
|
|
1881
|
+
const inputSchema = bodySchema ?? querySchema;
|
|
1882
|
+
if (inputSchema && hasInputExample) {
|
|
1883
|
+
const result = inputSchema.safeParse(inputExample);
|
|
1884
|
+
if (!result.success) {
|
|
1885
|
+
const issues = result.error.issues.map((i) => ` \u2022 ${i.path.length ? i.path.join(".") : "<root>"}: ${i.message}`).join("\n");
|
|
1886
|
+
throw new Error(
|
|
1887
|
+
`route '${key}': .inputExample() does not satisfy ${bodySchema ? ".body()" : ".query()"} schema:
|
|
1888
|
+
${issues}`
|
|
1889
|
+
);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
if (outputSchema && hasOutputExample) {
|
|
1893
|
+
const result = outputSchema.safeParse(outputExample);
|
|
1894
|
+
if (!result.success) {
|
|
1895
|
+
const issues = result.error.issues.map((i) => ` \u2022 ${i.path.length ? i.path.join(".") : "<root>"}: ${i.message}`).join("\n");
|
|
1896
|
+
throw new Error(
|
|
1897
|
+
`route '${key}': .outputExample() does not satisfy .output() schema:
|
|
1898
|
+
${issues}`
|
|
1899
|
+
);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1855
1904
|
// src/builder.ts
|
|
1856
1905
|
var RouteBuilder = class {
|
|
1857
1906
|
/** @internal */
|
|
@@ -1881,6 +1930,14 @@ var RouteBuilder = class {
|
|
|
1881
1930
|
/** @internal */
|
|
1882
1931
|
_outputSchema;
|
|
1883
1932
|
/** @internal */
|
|
1933
|
+
_inputExample = void 0;
|
|
1934
|
+
/** @internal */
|
|
1935
|
+
_hasInputExample = false;
|
|
1936
|
+
/** @internal */
|
|
1937
|
+
_outputExample = void 0;
|
|
1938
|
+
/** @internal */
|
|
1939
|
+
_hasOutputExample = false;
|
|
1940
|
+
/** @internal */
|
|
1884
1941
|
_description;
|
|
1885
1942
|
/** @internal */
|
|
1886
1943
|
_path;
|
|
@@ -2010,6 +2067,63 @@ var RouteBuilder = class {
|
|
|
2010
2067
|
next._outputSchema = schema;
|
|
2011
2068
|
return next;
|
|
2012
2069
|
}
|
|
2070
|
+
/**
|
|
2071
|
+
* Provide a conforming example of the request input (body or query params).
|
|
2072
|
+
*
|
|
2073
|
+
* **Required** whenever `.body()` or `.query()` is set — enforced at compile time via
|
|
2074
|
+
* `.handler()` overloads, and at route-registration time via Zod validation of the
|
|
2075
|
+
* example against the schema. The example is embedded in the bazaar discovery extension
|
|
2076
|
+
* so indexers can advertise a working sample call.
|
|
2077
|
+
*
|
|
2078
|
+
* @example
|
|
2079
|
+
* ```ts
|
|
2080
|
+
* router.route('search')
|
|
2081
|
+
* .paid('0.01')
|
|
2082
|
+
* .body(z.object({ q: z.string() }))
|
|
2083
|
+
* .inputExample({ q: 'hello world' })
|
|
2084
|
+
* .handler(async ({ body }) => { ... });
|
|
2085
|
+
* ```
|
|
2086
|
+
*/
|
|
2087
|
+
inputExample(example) {
|
|
2088
|
+
const next = this.fork();
|
|
2089
|
+
next._inputExample = example;
|
|
2090
|
+
next._hasInputExample = true;
|
|
2091
|
+
return next;
|
|
2092
|
+
}
|
|
2093
|
+
/**
|
|
2094
|
+
* Provide a conforming example of the response output.
|
|
2095
|
+
*
|
|
2096
|
+
* **Required** whenever `.output()` is set — enforced at compile time via `.handler()`
|
|
2097
|
+
* overloads, and at route-registration time via Zod validation of the example against
|
|
2098
|
+
* the schema. The example is embedded in the bazaar discovery extension so indexers
|
|
2099
|
+
* can advertise the response shape.
|
|
2100
|
+
*
|
|
2101
|
+
* Accepts any JSON value (objects, arrays, or primitives) — top-level array
|
|
2102
|
+
* or primitive responses (e.g. `z.array(...)`) are supported alongside the
|
|
2103
|
+
* common object case.
|
|
2104
|
+
*
|
|
2105
|
+
* @example
|
|
2106
|
+
* ```ts
|
|
2107
|
+
* router.route('search')
|
|
2108
|
+
* .paid('0.01')
|
|
2109
|
+
* .output(z.object({ results: z.array(z.string()) }))
|
|
2110
|
+
* .outputExample({ results: ['a', 'b'] })
|
|
2111
|
+
* .handler(async () => { ... });
|
|
2112
|
+
*
|
|
2113
|
+
* // Top-level array response
|
|
2114
|
+
* router.route('chains')
|
|
2115
|
+
* .paid('0.01')
|
|
2116
|
+
* .output(z.array(z.object({ name: z.string() })))
|
|
2117
|
+
* .outputExample([{ name: 'Ethereum' }])
|
|
2118
|
+
* .handler(async () => { ... });
|
|
2119
|
+
* ```
|
|
2120
|
+
*/
|
|
2121
|
+
outputExample(example) {
|
|
2122
|
+
const next = this.fork();
|
|
2123
|
+
next._outputExample = example;
|
|
2124
|
+
next._hasOutputExample = true;
|
|
2125
|
+
return next;
|
|
2126
|
+
}
|
|
2013
2127
|
description(text) {
|
|
2014
2128
|
const next = this.fork();
|
|
2015
2129
|
next._description = text;
|
|
@@ -2054,12 +2168,26 @@ var RouteBuilder = class {
|
|
|
2054
2168
|
next._validateFn = fn;
|
|
2055
2169
|
return next;
|
|
2056
2170
|
}
|
|
2171
|
+
// -------------------------------------------------------------------------
|
|
2172
|
+
// Terminal method
|
|
2173
|
+
// -------------------------------------------------------------------------
|
|
2057
2174
|
handler(fn) {
|
|
2175
|
+
const handlerFn = fn;
|
|
2058
2176
|
if (this._validateFn && !this._bodySchema) {
|
|
2059
2177
|
throw new Error(
|
|
2060
2178
|
`route '${this._key}': .validate() requires .body() \u2014 validation runs on parsed body`
|
|
2061
2179
|
);
|
|
2062
2180
|
}
|
|
2181
|
+
validateExamples(
|
|
2182
|
+
this._key,
|
|
2183
|
+
this._bodySchema,
|
|
2184
|
+
this._querySchema,
|
|
2185
|
+
this._outputSchema,
|
|
2186
|
+
this._inputExample,
|
|
2187
|
+
this._hasInputExample,
|
|
2188
|
+
this._outputExample,
|
|
2189
|
+
this._hasOutputExample
|
|
2190
|
+
);
|
|
2063
2191
|
const entry = {
|
|
2064
2192
|
key: this._key,
|
|
2065
2193
|
authMode: this._authMode,
|
|
@@ -2069,6 +2197,8 @@ var RouteBuilder = class {
|
|
|
2069
2197
|
bodySchema: this._bodySchema,
|
|
2070
2198
|
querySchema: this._querySchema,
|
|
2071
2199
|
outputSchema: this._outputSchema,
|
|
2200
|
+
inputExample: this._hasInputExample ? this._inputExample : void 0,
|
|
2201
|
+
outputExample: this._hasOutputExample ? this._outputExample : void 0,
|
|
2072
2202
|
description: this._description,
|
|
2073
2203
|
path: this._path,
|
|
2074
2204
|
method: this._method,
|
|
@@ -2082,7 +2212,11 @@ var RouteBuilder = class {
|
|
|
2082
2212
|
mppInfo: this._mppInfo
|
|
2083
2213
|
};
|
|
2084
2214
|
this._registry.register(entry);
|
|
2085
|
-
return createRequestHandler(
|
|
2215
|
+
return createRequestHandler(
|
|
2216
|
+
entry,
|
|
2217
|
+
handlerFn,
|
|
2218
|
+
this._deps
|
|
2219
|
+
);
|
|
2086
2220
|
}
|
|
2087
2221
|
};
|
|
2088
2222
|
|