@agentcash/router 1.3.3 → 1.4.0
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 +133 -2
- package/dist/index.d.cts +114 -16
- package/dist/index.d.ts +114 -16
- package/dist/index.js +133 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1769,10 +1769,16 @@ async function build402(request, routeEntry, deps, meta, pluginCtx, bodyData) {
|
|
|
1769
1769
|
const outputSchema = routeEntry.outputSchema ? toJSON(routeEntry.outputSchema) : void 0;
|
|
1770
1770
|
if (inputSchema) {
|
|
1771
1771
|
const config = {
|
|
1772
|
+
method: routeEntry.method,
|
|
1772
1773
|
bodyType: routeEntry.bodySchema ? "json" : void 0,
|
|
1773
1774
|
inputSchema
|
|
1774
1775
|
};
|
|
1775
|
-
if (
|
|
1776
|
+
if (routeEntry.inputExample !== void 0) {
|
|
1777
|
+
config.input = routeEntry.inputExample;
|
|
1778
|
+
}
|
|
1779
|
+
if (outputSchema && routeEntry.outputExample !== void 0) {
|
|
1780
|
+
config.output = { schema: outputSchema, example: routeEntry.outputExample };
|
|
1781
|
+
}
|
|
1776
1782
|
extensions = declareDiscoveryExtension(config);
|
|
1777
1783
|
}
|
|
1778
1784
|
} catch (err) {
|
|
@@ -1891,6 +1897,46 @@ function fireProviderQuota(routeEntry, response, handlerResult, deps, pluginCtx)
|
|
|
1891
1897
|
}
|
|
1892
1898
|
}
|
|
1893
1899
|
|
|
1900
|
+
// src/validate-examples.ts
|
|
1901
|
+
function validateExamples(key, bodySchema, querySchema, outputSchema, inputExample, hasInputExample, outputExample, hasOutputExample) {
|
|
1902
|
+
if (bodySchema && !hasInputExample) {
|
|
1903
|
+
throw new Error(
|
|
1904
|
+
`route '${key}': .body() requires a matching .inputExample() \u2014 the bazaar discovery extension needs a conforming sample body to advertise.`
|
|
1905
|
+
);
|
|
1906
|
+
}
|
|
1907
|
+
if (querySchema && !hasInputExample) {
|
|
1908
|
+
throw new Error(
|
|
1909
|
+
`route '${key}': .query() requires a matching .inputExample() \u2014 the bazaar discovery extension needs a conforming sample query to advertise.`
|
|
1910
|
+
);
|
|
1911
|
+
}
|
|
1912
|
+
if (outputSchema && !hasOutputExample) {
|
|
1913
|
+
throw new Error(
|
|
1914
|
+
`route '${key}': .output() requires a matching .outputExample() \u2014 the bazaar discovery extension needs a conforming sample response to advertise.`
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
const inputSchema = bodySchema ?? querySchema;
|
|
1918
|
+
if (inputSchema && hasInputExample) {
|
|
1919
|
+
const result = inputSchema.safeParse(inputExample);
|
|
1920
|
+
if (!result.success) {
|
|
1921
|
+
const issues = result.error.issues.map((i) => ` \u2022 ${i.path.length ? i.path.join(".") : "<root>"}: ${i.message}`).join("\n");
|
|
1922
|
+
throw new Error(
|
|
1923
|
+
`route '${key}': .inputExample() does not satisfy ${bodySchema ? ".body()" : ".query()"} schema:
|
|
1924
|
+
${issues}`
|
|
1925
|
+
);
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
if (outputSchema && hasOutputExample) {
|
|
1929
|
+
const result = outputSchema.safeParse(outputExample);
|
|
1930
|
+
if (!result.success) {
|
|
1931
|
+
const issues = result.error.issues.map((i) => ` \u2022 ${i.path.length ? i.path.join(".") : "<root>"}: ${i.message}`).join("\n");
|
|
1932
|
+
throw new Error(
|
|
1933
|
+
`route '${key}': .outputExample() does not satisfy .output() schema:
|
|
1934
|
+
${issues}`
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1894
1940
|
// src/builder.ts
|
|
1895
1941
|
var RouteBuilder = class {
|
|
1896
1942
|
/** @internal */
|
|
@@ -1920,6 +1966,14 @@ var RouteBuilder = class {
|
|
|
1920
1966
|
/** @internal */
|
|
1921
1967
|
_outputSchema;
|
|
1922
1968
|
/** @internal */
|
|
1969
|
+
_inputExample = void 0;
|
|
1970
|
+
/** @internal */
|
|
1971
|
+
_hasInputExample = false;
|
|
1972
|
+
/** @internal */
|
|
1973
|
+
_outputExample = void 0;
|
|
1974
|
+
/** @internal */
|
|
1975
|
+
_hasOutputExample = false;
|
|
1976
|
+
/** @internal */
|
|
1923
1977
|
_description;
|
|
1924
1978
|
/** @internal */
|
|
1925
1979
|
_path;
|
|
@@ -2049,6 +2103,63 @@ var RouteBuilder = class {
|
|
|
2049
2103
|
next._outputSchema = schema;
|
|
2050
2104
|
return next;
|
|
2051
2105
|
}
|
|
2106
|
+
/**
|
|
2107
|
+
* Provide a conforming example of the request input (body or query params).
|
|
2108
|
+
*
|
|
2109
|
+
* **Required** whenever `.body()` or `.query()` is set — enforced at compile time via
|
|
2110
|
+
* `.handler()` overloads, and at route-registration time via Zod validation of the
|
|
2111
|
+
* example against the schema. The example is embedded in the bazaar discovery extension
|
|
2112
|
+
* so indexers can advertise a working sample call.
|
|
2113
|
+
*
|
|
2114
|
+
* @example
|
|
2115
|
+
* ```ts
|
|
2116
|
+
* router.route('search')
|
|
2117
|
+
* .paid('0.01')
|
|
2118
|
+
* .body(z.object({ q: z.string() }))
|
|
2119
|
+
* .inputExample({ q: 'hello world' })
|
|
2120
|
+
* .handler(async ({ body }) => { ... });
|
|
2121
|
+
* ```
|
|
2122
|
+
*/
|
|
2123
|
+
inputExample(example) {
|
|
2124
|
+
const next = this.fork();
|
|
2125
|
+
next._inputExample = example;
|
|
2126
|
+
next._hasInputExample = true;
|
|
2127
|
+
return next;
|
|
2128
|
+
}
|
|
2129
|
+
/**
|
|
2130
|
+
* Provide a conforming example of the response output.
|
|
2131
|
+
*
|
|
2132
|
+
* **Required** whenever `.output()` is set — enforced at compile time via `.handler()`
|
|
2133
|
+
* overloads, and at route-registration time via Zod validation of the example against
|
|
2134
|
+
* the schema. The example is embedded in the bazaar discovery extension so indexers
|
|
2135
|
+
* can advertise the response shape.
|
|
2136
|
+
*
|
|
2137
|
+
* Accepts any JSON value (objects, arrays, or primitives) — top-level array
|
|
2138
|
+
* or primitive responses (e.g. `z.array(...)`) are supported alongside the
|
|
2139
|
+
* common object case.
|
|
2140
|
+
*
|
|
2141
|
+
* @example
|
|
2142
|
+
* ```ts
|
|
2143
|
+
* router.route('search')
|
|
2144
|
+
* .paid('0.01')
|
|
2145
|
+
* .output(z.object({ results: z.array(z.string()) }))
|
|
2146
|
+
* .outputExample({ results: ['a', 'b'] })
|
|
2147
|
+
* .handler(async () => { ... });
|
|
2148
|
+
*
|
|
2149
|
+
* // Top-level array response
|
|
2150
|
+
* router.route('chains')
|
|
2151
|
+
* .paid('0.01')
|
|
2152
|
+
* .output(z.array(z.object({ name: z.string() })))
|
|
2153
|
+
* .outputExample([{ name: 'Ethereum' }])
|
|
2154
|
+
* .handler(async () => { ... });
|
|
2155
|
+
* ```
|
|
2156
|
+
*/
|
|
2157
|
+
outputExample(example) {
|
|
2158
|
+
const next = this.fork();
|
|
2159
|
+
next._outputExample = example;
|
|
2160
|
+
next._hasOutputExample = true;
|
|
2161
|
+
return next;
|
|
2162
|
+
}
|
|
2052
2163
|
description(text) {
|
|
2053
2164
|
const next = this.fork();
|
|
2054
2165
|
next._description = text;
|
|
@@ -2093,12 +2204,26 @@ var RouteBuilder = class {
|
|
|
2093
2204
|
next._validateFn = fn;
|
|
2094
2205
|
return next;
|
|
2095
2206
|
}
|
|
2207
|
+
// -------------------------------------------------------------------------
|
|
2208
|
+
// Terminal method
|
|
2209
|
+
// -------------------------------------------------------------------------
|
|
2096
2210
|
handler(fn) {
|
|
2211
|
+
const handlerFn = fn;
|
|
2097
2212
|
if (this._validateFn && !this._bodySchema) {
|
|
2098
2213
|
throw new Error(
|
|
2099
2214
|
`route '${this._key}': .validate() requires .body() \u2014 validation runs on parsed body`
|
|
2100
2215
|
);
|
|
2101
2216
|
}
|
|
2217
|
+
validateExamples(
|
|
2218
|
+
this._key,
|
|
2219
|
+
this._bodySchema,
|
|
2220
|
+
this._querySchema,
|
|
2221
|
+
this._outputSchema,
|
|
2222
|
+
this._inputExample,
|
|
2223
|
+
this._hasInputExample,
|
|
2224
|
+
this._outputExample,
|
|
2225
|
+
this._hasOutputExample
|
|
2226
|
+
);
|
|
2102
2227
|
const entry = {
|
|
2103
2228
|
key: this._key,
|
|
2104
2229
|
authMode: this._authMode,
|
|
@@ -2108,6 +2233,8 @@ var RouteBuilder = class {
|
|
|
2108
2233
|
bodySchema: this._bodySchema,
|
|
2109
2234
|
querySchema: this._querySchema,
|
|
2110
2235
|
outputSchema: this._outputSchema,
|
|
2236
|
+
inputExample: this._hasInputExample ? this._inputExample : void 0,
|
|
2237
|
+
outputExample: this._hasOutputExample ? this._outputExample : void 0,
|
|
2111
2238
|
description: this._description,
|
|
2112
2239
|
path: this._path,
|
|
2113
2240
|
method: this._method,
|
|
@@ -2121,7 +2248,11 @@ var RouteBuilder = class {
|
|
|
2121
2248
|
mppInfo: this._mppInfo
|
|
2122
2249
|
};
|
|
2123
2250
|
this._registry.register(entry);
|
|
2124
|
-
return createRequestHandler(
|
|
2251
|
+
return createRequestHandler(
|
|
2252
|
+
entry,
|
|
2253
|
+
handlerFn,
|
|
2254
|
+
this._deps
|
|
2255
|
+
);
|
|
2125
2256
|
}
|
|
2126
2257
|
};
|
|
2127
2258
|
|
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
|
@@ -1730,10 +1730,16 @@ async function build402(request, routeEntry, deps, meta, pluginCtx, bodyData) {
|
|
|
1730
1730
|
const outputSchema = routeEntry.outputSchema ? toJSON(routeEntry.outputSchema) : void 0;
|
|
1731
1731
|
if (inputSchema) {
|
|
1732
1732
|
const config = {
|
|
1733
|
+
method: routeEntry.method,
|
|
1733
1734
|
bodyType: routeEntry.bodySchema ? "json" : void 0,
|
|
1734
1735
|
inputSchema
|
|
1735
1736
|
};
|
|
1736
|
-
if (
|
|
1737
|
+
if (routeEntry.inputExample !== void 0) {
|
|
1738
|
+
config.input = routeEntry.inputExample;
|
|
1739
|
+
}
|
|
1740
|
+
if (outputSchema && routeEntry.outputExample !== void 0) {
|
|
1741
|
+
config.output = { schema: outputSchema, example: routeEntry.outputExample };
|
|
1742
|
+
}
|
|
1737
1743
|
extensions = declareDiscoveryExtension(config);
|
|
1738
1744
|
}
|
|
1739
1745
|
} catch (err) {
|
|
@@ -1852,6 +1858,46 @@ function fireProviderQuota(routeEntry, response, handlerResult, deps, pluginCtx)
|
|
|
1852
1858
|
}
|
|
1853
1859
|
}
|
|
1854
1860
|
|
|
1861
|
+
// src/validate-examples.ts
|
|
1862
|
+
function validateExamples(key, bodySchema, querySchema, outputSchema, inputExample, hasInputExample, outputExample, hasOutputExample) {
|
|
1863
|
+
if (bodySchema && !hasInputExample) {
|
|
1864
|
+
throw new Error(
|
|
1865
|
+
`route '${key}': .body() requires a matching .inputExample() \u2014 the bazaar discovery extension needs a conforming sample body to advertise.`
|
|
1866
|
+
);
|
|
1867
|
+
}
|
|
1868
|
+
if (querySchema && !hasInputExample) {
|
|
1869
|
+
throw new Error(
|
|
1870
|
+
`route '${key}': .query() requires a matching .inputExample() \u2014 the bazaar discovery extension needs a conforming sample query to advertise.`
|
|
1871
|
+
);
|
|
1872
|
+
}
|
|
1873
|
+
if (outputSchema && !hasOutputExample) {
|
|
1874
|
+
throw new Error(
|
|
1875
|
+
`route '${key}': .output() requires a matching .outputExample() \u2014 the bazaar discovery extension needs a conforming sample response to advertise.`
|
|
1876
|
+
);
|
|
1877
|
+
}
|
|
1878
|
+
const inputSchema = bodySchema ?? querySchema;
|
|
1879
|
+
if (inputSchema && hasInputExample) {
|
|
1880
|
+
const result = inputSchema.safeParse(inputExample);
|
|
1881
|
+
if (!result.success) {
|
|
1882
|
+
const issues = result.error.issues.map((i) => ` \u2022 ${i.path.length ? i.path.join(".") : "<root>"}: ${i.message}`).join("\n");
|
|
1883
|
+
throw new Error(
|
|
1884
|
+
`route '${key}': .inputExample() does not satisfy ${bodySchema ? ".body()" : ".query()"} schema:
|
|
1885
|
+
${issues}`
|
|
1886
|
+
);
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
if (outputSchema && hasOutputExample) {
|
|
1890
|
+
const result = outputSchema.safeParse(outputExample);
|
|
1891
|
+
if (!result.success) {
|
|
1892
|
+
const issues = result.error.issues.map((i) => ` \u2022 ${i.path.length ? i.path.join(".") : "<root>"}: ${i.message}`).join("\n");
|
|
1893
|
+
throw new Error(
|
|
1894
|
+
`route '${key}': .outputExample() does not satisfy .output() schema:
|
|
1895
|
+
${issues}`
|
|
1896
|
+
);
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1855
1901
|
// src/builder.ts
|
|
1856
1902
|
var RouteBuilder = class {
|
|
1857
1903
|
/** @internal */
|
|
@@ -1881,6 +1927,14 @@ var RouteBuilder = class {
|
|
|
1881
1927
|
/** @internal */
|
|
1882
1928
|
_outputSchema;
|
|
1883
1929
|
/** @internal */
|
|
1930
|
+
_inputExample = void 0;
|
|
1931
|
+
/** @internal */
|
|
1932
|
+
_hasInputExample = false;
|
|
1933
|
+
/** @internal */
|
|
1934
|
+
_outputExample = void 0;
|
|
1935
|
+
/** @internal */
|
|
1936
|
+
_hasOutputExample = false;
|
|
1937
|
+
/** @internal */
|
|
1884
1938
|
_description;
|
|
1885
1939
|
/** @internal */
|
|
1886
1940
|
_path;
|
|
@@ -2010,6 +2064,63 @@ var RouteBuilder = class {
|
|
|
2010
2064
|
next._outputSchema = schema;
|
|
2011
2065
|
return next;
|
|
2012
2066
|
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Provide a conforming example of the request input (body or query params).
|
|
2069
|
+
*
|
|
2070
|
+
* **Required** whenever `.body()` or `.query()` is set — enforced at compile time via
|
|
2071
|
+
* `.handler()` overloads, and at route-registration time via Zod validation of the
|
|
2072
|
+
* example against the schema. The example is embedded in the bazaar discovery extension
|
|
2073
|
+
* so indexers can advertise a working sample call.
|
|
2074
|
+
*
|
|
2075
|
+
* @example
|
|
2076
|
+
* ```ts
|
|
2077
|
+
* router.route('search')
|
|
2078
|
+
* .paid('0.01')
|
|
2079
|
+
* .body(z.object({ q: z.string() }))
|
|
2080
|
+
* .inputExample({ q: 'hello world' })
|
|
2081
|
+
* .handler(async ({ body }) => { ... });
|
|
2082
|
+
* ```
|
|
2083
|
+
*/
|
|
2084
|
+
inputExample(example) {
|
|
2085
|
+
const next = this.fork();
|
|
2086
|
+
next._inputExample = example;
|
|
2087
|
+
next._hasInputExample = true;
|
|
2088
|
+
return next;
|
|
2089
|
+
}
|
|
2090
|
+
/**
|
|
2091
|
+
* Provide a conforming example of the response output.
|
|
2092
|
+
*
|
|
2093
|
+
* **Required** whenever `.output()` is set — enforced at compile time via `.handler()`
|
|
2094
|
+
* overloads, and at route-registration time via Zod validation of the example against
|
|
2095
|
+
* the schema. The example is embedded in the bazaar discovery extension so indexers
|
|
2096
|
+
* can advertise the response shape.
|
|
2097
|
+
*
|
|
2098
|
+
* Accepts any JSON value (objects, arrays, or primitives) — top-level array
|
|
2099
|
+
* or primitive responses (e.g. `z.array(...)`) are supported alongside the
|
|
2100
|
+
* common object case.
|
|
2101
|
+
*
|
|
2102
|
+
* @example
|
|
2103
|
+
* ```ts
|
|
2104
|
+
* router.route('search')
|
|
2105
|
+
* .paid('0.01')
|
|
2106
|
+
* .output(z.object({ results: z.array(z.string()) }))
|
|
2107
|
+
* .outputExample({ results: ['a', 'b'] })
|
|
2108
|
+
* .handler(async () => { ... });
|
|
2109
|
+
*
|
|
2110
|
+
* // Top-level array response
|
|
2111
|
+
* router.route('chains')
|
|
2112
|
+
* .paid('0.01')
|
|
2113
|
+
* .output(z.array(z.object({ name: z.string() })))
|
|
2114
|
+
* .outputExample([{ name: 'Ethereum' }])
|
|
2115
|
+
* .handler(async () => { ... });
|
|
2116
|
+
* ```
|
|
2117
|
+
*/
|
|
2118
|
+
outputExample(example) {
|
|
2119
|
+
const next = this.fork();
|
|
2120
|
+
next._outputExample = example;
|
|
2121
|
+
next._hasOutputExample = true;
|
|
2122
|
+
return next;
|
|
2123
|
+
}
|
|
2013
2124
|
description(text) {
|
|
2014
2125
|
const next = this.fork();
|
|
2015
2126
|
next._description = text;
|
|
@@ -2054,12 +2165,26 @@ var RouteBuilder = class {
|
|
|
2054
2165
|
next._validateFn = fn;
|
|
2055
2166
|
return next;
|
|
2056
2167
|
}
|
|
2168
|
+
// -------------------------------------------------------------------------
|
|
2169
|
+
// Terminal method
|
|
2170
|
+
// -------------------------------------------------------------------------
|
|
2057
2171
|
handler(fn) {
|
|
2172
|
+
const handlerFn = fn;
|
|
2058
2173
|
if (this._validateFn && !this._bodySchema) {
|
|
2059
2174
|
throw new Error(
|
|
2060
2175
|
`route '${this._key}': .validate() requires .body() \u2014 validation runs on parsed body`
|
|
2061
2176
|
);
|
|
2062
2177
|
}
|
|
2178
|
+
validateExamples(
|
|
2179
|
+
this._key,
|
|
2180
|
+
this._bodySchema,
|
|
2181
|
+
this._querySchema,
|
|
2182
|
+
this._outputSchema,
|
|
2183
|
+
this._inputExample,
|
|
2184
|
+
this._hasInputExample,
|
|
2185
|
+
this._outputExample,
|
|
2186
|
+
this._hasOutputExample
|
|
2187
|
+
);
|
|
2063
2188
|
const entry = {
|
|
2064
2189
|
key: this._key,
|
|
2065
2190
|
authMode: this._authMode,
|
|
@@ -2069,6 +2194,8 @@ var RouteBuilder = class {
|
|
|
2069
2194
|
bodySchema: this._bodySchema,
|
|
2070
2195
|
querySchema: this._querySchema,
|
|
2071
2196
|
outputSchema: this._outputSchema,
|
|
2197
|
+
inputExample: this._hasInputExample ? this._inputExample : void 0,
|
|
2198
|
+
outputExample: this._hasOutputExample ? this._outputExample : void 0,
|
|
2072
2199
|
description: this._description,
|
|
2073
2200
|
path: this._path,
|
|
2074
2201
|
method: this._method,
|
|
@@ -2082,7 +2209,11 @@ var RouteBuilder = class {
|
|
|
2082
2209
|
mppInfo: this._mppInfo
|
|
2083
2210
|
};
|
|
2084
2211
|
this._registry.register(entry);
|
|
2085
|
-
return createRequestHandler(
|
|
2212
|
+
return createRequestHandler(
|
|
2213
|
+
entry,
|
|
2214
|
+
handlerFn,
|
|
2215
|
+
this._deps
|
|
2216
|
+
);
|
|
2086
2217
|
}
|
|
2087
2218
|
};
|
|
2088
2219
|
|