@fresho/router 0.6.0 → 0.6.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/README.md +4 -1
- package/dist/{docs-B1IFBS_w.mjs → docs-C77XQzF3.mjs} +49 -35
- package/dist/docs-C77XQzF3.mjs.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/mcp/index.d.mts +1 -1
- package/dist/mcp/index.d.mts.map +1 -1
- package/dist/mcp/index.mjs +7 -3
- package/dist/mcp/index.mjs.map +1 -1
- package/dist/middleware/index.d.mts +1 -1
- package/dist/{middleware-zRMBpTLe.d.mts → middleware-Cj1yHSJF.d.mts} +12 -11
- package/dist/middleware-Cj1yHSJF.d.mts.map +1 -0
- package/package.json +1 -1
- package/dist/docs-B1IFBS_w.mjs.map +0 -1
- package/dist/middleware-zRMBpTLe.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -105,6 +105,9 @@ Define query and body schemas using shorthand syntax:
|
|
|
105
105
|
{ tags: 'string[]' } // string array
|
|
106
106
|
{ scores: 'number[]' } // number array
|
|
107
107
|
|
|
108
|
+
// Field descriptions (for OpenAPI and MCP tools)
|
|
109
|
+
{ folder: ['string?', 'Mailbox folder, INBOX by default'] }
|
|
110
|
+
|
|
108
111
|
// Nested objects
|
|
109
112
|
{
|
|
110
113
|
address: {
|
|
@@ -411,7 +414,7 @@ const api = router({
|
|
|
411
414
|
`mcp()` requires endpoint middleware (usually auth). Pass `public: true` instead when the endpoint is open, or already guarded by middleware on its parent routers.
|
|
412
415
|
|
|
413
416
|
- **Tools** — one per route, named from the method and path (`get_users`, `post_users`, `get_users_by_id`, ...) unless the route sets `operationId` (also used in OpenAPI). Descriptions come from `route.description`.
|
|
414
|
-
- **Arguments** — flat: path params, query fields and body fields side by side (`{ id, text }`), as JSON Schema derived from the route's schemas. Names must be unique across the three; a clash throws when `mcp()` is called. `
|
|
417
|
+
- **Arguments** — flat: path params, query fields and body fields side by side (`{ id, text }`), as JSON Schema derived from the route's schemas. Names must be unique across the three; a clash throws when `mcp()` is called. Use `[type, description]` tuples (e.g. `{ folder: ['string?', 'Mailbox folder'] }`) in `query` and `body` schemas to document fields for agents and OpenAPI. Array query params are left out, since the router can't parse them from query strings. Without a body schema, `post`/`put`/`patch` tools send any other arguments as the body.
|
|
415
418
|
- **Annotations** — `get` tools are marked `readOnlyHint`, `delete` tools `destructiveHint` and `idempotentHint`, and `put` tools `idempotentHint`, so clients can decide when to ask for confirmation. Override per route with `mcp: { annotations: { ... } }`.
|
|
416
419
|
- **Calls** — each tool call is dispatched as an HTTP request through the given routers with the caller's headers forwarded. Middleware on the MCP endpoint (passed to `mcp()` or on its parent routers) runs once per MCP request, and what it adds to the context (e.g. `c.user`) reaches handlers; middleware on the given routers and below runs on every call. Middleware on the given routers' *own* parents elsewhere in your app does **not** run, so guard the endpoint or the routers themselves. Responses become text content, plus `structuredContent` when the body is a JSON object; a non-2xx status sets `isError`, and streaming (SSE, NDJSON) responses are rejected.
|
|
417
420
|
- **Safety** — requests with an `Origin` other than the endpoint's get 403 (as the MCP spec requires against DNS rebinding), so browser-hosted clients on other origins can't connect. Path parameters can't be `.` or `..`, and handler exceptions become a generic `Internal error`.
|
|
@@ -8,9 +8,13 @@ const PRIMITIVE_TYPES = [
|
|
|
8
8
|
"boolean",
|
|
9
9
|
"unknown"
|
|
10
10
|
];
|
|
11
|
-
/** Checks if a schema field is a nested object (not a type string). */
|
|
11
|
+
/** Checks if a schema field is a nested object (not a type string or [type, description] tuple). */
|
|
12
12
|
function isNestedSchema(field) {
|
|
13
|
-
return typeof field === "object" && field !== null;
|
|
13
|
+
return typeof field === "object" && field !== null && !Array.isArray(field);
|
|
14
|
+
}
|
|
15
|
+
/** Unpacks a schema field into its type string and optional description. */
|
|
16
|
+
function unpackField(field) {
|
|
17
|
+
return Array.isArray(field) ? [field[0], field[1]] : [field, void 0];
|
|
14
18
|
}
|
|
15
19
|
/**
|
|
16
20
|
* Validates and coerces a single primitive value.
|
|
@@ -18,14 +22,25 @@ function isNestedSchema(field) {
|
|
|
18
22
|
*/
|
|
19
23
|
function validatePrimitive(value, baseType) {
|
|
20
24
|
switch (baseType) {
|
|
21
|
-
case "string":
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
case "string":
|
|
26
|
+
if (typeof value !== "string") return [
|
|
27
|
+
false,
|
|
28
|
+
void 0,
|
|
29
|
+
"Expected string"
|
|
30
|
+
];
|
|
31
|
+
return [
|
|
32
|
+
true,
|
|
33
|
+
value,
|
|
34
|
+
null
|
|
35
|
+
];
|
|
26
36
|
case "number": {
|
|
37
|
+
if (typeof value !== "number" && typeof value !== "string" || typeof value === "string" && value.trim() === "") return [
|
|
38
|
+
false,
|
|
39
|
+
void 0,
|
|
40
|
+
"Expected number"
|
|
41
|
+
];
|
|
27
42
|
const num = Number(value);
|
|
28
|
-
if (Number.
|
|
43
|
+
if (!Number.isFinite(num)) return [
|
|
29
44
|
false,
|
|
30
45
|
void 0,
|
|
31
46
|
"Expected number"
|
|
@@ -87,12 +102,12 @@ function validateField(key, field, value, parentPath = "") {
|
|
|
87
102
|
nestedResult.errors
|
|
88
103
|
];
|
|
89
104
|
}
|
|
90
|
-
const typeStr = field;
|
|
105
|
+
const [typeStr] = unpackField(field);
|
|
91
106
|
const isOptional = typeStr.endsWith("?");
|
|
92
107
|
const isArray = typeStr.includes("[]");
|
|
93
108
|
let baseType = isOptional ? typeStr.slice(0, -1) : typeStr;
|
|
94
109
|
if (isArray) baseType = baseType.replace("[]", "");
|
|
95
|
-
if (value === void 0 || value === "") {
|
|
110
|
+
if (value === void 0 || value === null || value === "") {
|
|
96
111
|
if (!isOptional) {
|
|
97
112
|
errors[fieldPath] = ["Required"];
|
|
98
113
|
return [
|
|
@@ -157,9 +172,9 @@ function validateField(key, field, value, parentPath = "") {
|
|
|
157
172
|
function validateSchema(schema, data, parentPath = "") {
|
|
158
173
|
const result = {};
|
|
159
174
|
const allErrors = {};
|
|
175
|
+
const source = typeof data === "object" && data !== null && !Array.isArray(data) ? data : {};
|
|
160
176
|
for (const [key, field] of Object.entries(schema)) {
|
|
161
|
-
const
|
|
162
|
-
const [success, coerced, fieldErrors] = validateField(key, field, value, parentPath);
|
|
177
|
+
const [success, coerced, fieldErrors] = validateField(key, field, Object.hasOwn(source, key) ? source[key] : void 0, parentPath);
|
|
163
178
|
if (!success) Object.assign(allErrors, fieldErrors);
|
|
164
179
|
else if (coerced !== void 0) result[key] = coerced;
|
|
165
180
|
}
|
|
@@ -175,7 +190,9 @@ function validateSchemaDefinition(schema, path = "") {
|
|
|
175
190
|
const fieldPath = path ? `${path}.${key}` : key;
|
|
176
191
|
if (isNestedSchema(field)) validateSchemaDefinition(field, fieldPath);
|
|
177
192
|
else {
|
|
178
|
-
|
|
193
|
+
if (Array.isArray(field) && (field.length !== 2 || typeof field[0] !== "string" || typeof field[1] !== "string")) throw new Error(`Invalid field definition at '${fieldPath}'`);
|
|
194
|
+
const [typeStr] = unpackField(field);
|
|
195
|
+
if (typeof typeStr !== "string") throw new Error(`Unknown type '${String(typeStr)}' at '${fieldPath}'`);
|
|
179
196
|
let baseType = typeStr.endsWith("?") ? typeStr.slice(0, -1) : typeStr;
|
|
180
197
|
baseType = baseType.replace("[]", "");
|
|
181
198
|
if (!PRIMITIVE_TYPES.includes(baseType)) throw new Error(`Unknown type '${typeStr}' at '${fieldPath}'`);
|
|
@@ -223,26 +240,20 @@ function compileSchema(schema) {
|
|
|
223
240
|
}
|
|
224
241
|
/**
|
|
225
242
|
* Converts a schema definition to JSON Schema, recursing into nested objects.
|
|
226
|
-
*
|
|
227
|
-
* @param descriptions - Field descriptions keyed by name, dotted for nested fields
|
|
228
|
-
* @param prefix - Dotted path of `schema` within the root, for nested lookups
|
|
229
243
|
*/
|
|
230
|
-
function toJsonSchema(schema
|
|
244
|
+
function toJsonSchema(schema) {
|
|
231
245
|
const properties = {};
|
|
232
246
|
const required = [];
|
|
233
247
|
for (const [key, field] of Object.entries(schema)) {
|
|
234
|
-
const description = descriptions[`${prefix}${key}`];
|
|
235
|
-
const described = description === void 0 ? {} : { description };
|
|
236
248
|
if (isNestedSchema(field)) {
|
|
237
|
-
properties[key] =
|
|
238
|
-
...toJsonSchema(field, descriptions, `${prefix}${key}.`),
|
|
239
|
-
...described
|
|
240
|
-
};
|
|
249
|
+
properties[key] = toJsonSchema(field);
|
|
241
250
|
required.push(key);
|
|
242
251
|
continue;
|
|
243
252
|
}
|
|
244
|
-
const
|
|
245
|
-
const
|
|
253
|
+
const [typeStr, description] = unpackField(field);
|
|
254
|
+
const described = description === void 0 ? {} : { description };
|
|
255
|
+
const optional = typeStr.endsWith("?");
|
|
256
|
+
const base = optional ? typeStr.slice(0, -1) : typeStr;
|
|
246
257
|
const primitive = base.replace("[]", "");
|
|
247
258
|
const item = primitive === "unknown" ? {} : { type: primitive };
|
|
248
259
|
properties[key] = base.endsWith("[]") ? {
|
|
@@ -690,13 +701,16 @@ function generateDocs(config) {
|
|
|
690
701
|
};
|
|
691
702
|
if (route$1?.query) {
|
|
692
703
|
const querySchema = route$1.query;
|
|
693
|
-
operation.parameters = Object.entries(querySchema).filter(([,
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
704
|
+
operation.parameters = Object.entries(querySchema).filter(([, field]) => !isNestedSchema(field)).map(([name, field]) => {
|
|
705
|
+
const [type, description] = unpackField(field);
|
|
706
|
+
return {
|
|
707
|
+
name,
|
|
708
|
+
in: "query",
|
|
709
|
+
required: !type.endsWith("?"),
|
|
710
|
+
description,
|
|
711
|
+
schema: { type: type.replace("?", "").replace("[]", "") }
|
|
712
|
+
};
|
|
713
|
+
});
|
|
700
714
|
}
|
|
701
715
|
if (route$1?.body && [
|
|
702
716
|
"post",
|
|
@@ -704,7 +718,7 @@ function generateDocs(config) {
|
|
|
704
718
|
"patch"
|
|
705
719
|
].includes(method)) operation.requestBody = {
|
|
706
720
|
required: true,
|
|
707
|
-
content: { "application/json": { schema: toJsonSchema(route$1.body
|
|
721
|
+
content: { "application/json": { schema: toJsonSchema(route$1.body) } }
|
|
708
722
|
};
|
|
709
723
|
paths[path][method] = operation;
|
|
710
724
|
}
|
|
@@ -720,5 +734,5 @@ function generateDocs(config) {
|
|
|
720
734
|
}
|
|
721
735
|
|
|
722
736
|
//#endregion
|
|
723
|
-
export { isFunction as a, compileSchema as c, router as i,
|
|
724
|
-
//# sourceMappingURL=docs-
|
|
737
|
+
export { isFunction as a, compileSchema as c, unpackField as d, router as i, isNestedSchema as l, generateDocs as n, isRoute as o, route as r, isRouter as s, collectRoutes as t, toJsonSchema as u };
|
|
738
|
+
//# sourceMappingURL=docs-C77XQzF3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-C77XQzF3.mjs","names":["errors: Record<string, string[]>","resultArray: unknown[]","success","coerced","errorMsg","result: Record<string, unknown>","allErrors: Record<string, string[]>","properties: Record<string, unknown>","required: string[]","paramNames: string[]","propertyToSegment","compiled: CompiledRoute[]","path: Record<string, string>","context: MiddlewareContext","query: unknown","body: unknown","collectedRoutes: CollectedRoute[]","paths: Record<string, Record<string, object>>","operation: Record<string, unknown>","route"],"sources":["../src/schema.ts","../src/types.ts","../src/handler.ts","../src/core.ts","../src/docs.ts"],"sourcesContent":["/**\n * @fileoverview Schema types and validation utilities.\n *\n * Provides shorthand schema definitions for validating query parameters and request bodies.\n * No external dependencies - uses simple string-based type definitions.\n *\n * Supported types:\n * - Primitives: `'string'`, `'number'`, `'boolean'`\n * - Optional: `'string?'`, `'number?'`, `'boolean?'`\n * - Arrays: `'string[]'`, `'number[]'`, `'boolean[]'`\n * - Optional arrays: `'string[]?'`, `'number[]?'`, `'boolean[]?'`\n * - Described fields: `['string', 'Description']`, `['number?', 'Optional count']`\n * - Nested objects: `{ field: { nested: 'string' } }`\n *\n * @example\n * ```typescript\n * // In route definitions\n * const api = router({\n * users: router({\n * post: route({\n * query: { page: 'number?', limit: ['number?', 'Max results'] },\n * body: {\n * name: ['string', 'Full name'],\n * age: 'number',\n * tags: 'string[]?',\n * address: { city: 'string', zip: 'string?' },\n * },\n * handler: async (c) => {\n * c.query.page; // number | undefined\n * c.body.name; // string\n * c.body.address.city // string\n * },\n * }),\n * }),\n * });\n * ```\n */\n\n/** Primitive type strings. */\nexport type PrimitiveType = 'string' | 'number' | 'boolean' | 'unknown';\n\n/** Optional primitive type strings. */\nexport type OptionalPrimitiveType = 'string?' | 'number?' | 'boolean?' | 'unknown?';\n\n/** Array type strings. */\nexport type ArrayType = 'string[]' | 'number[]' | 'boolean[]' | 'unknown[]';\n\n/** Optional array type strings. */\nexport type OptionalArrayType = 'string[]?' | 'number[]?' | 'boolean[]?' | 'unknown[]?';\n\n/** All supported type strings. */\nexport type SchemaType = PrimitiveType | OptionalPrimitiveType | ArrayType | OptionalArrayType;\n\n/** A schema field: a type string, or a `[type, description]` tuple. */\nexport type SchemaField = SchemaType | readonly [type: SchemaType, description: string];\n\n/** Schema definition using shorthand syntax. Supports nested objects. */\nexport interface SchemaDefinition {\n [key: string]: SchemaField | SchemaDefinition;\n}\n\n/** Maps primitive type strings to TypeScript types. */\nexport type SchemaTypeMap = {\n string: string;\n 'string?': string | undefined;\n number: number;\n 'number?': number | undefined;\n boolean: boolean;\n 'boolean?': boolean | undefined;\n unknown: unknown;\n 'unknown?': unknown | undefined;\n 'string[]': string[];\n 'string[]?': string[] | undefined;\n 'number[]': number[];\n 'number[]?': number[] | undefined;\n 'boolean[]': boolean[];\n 'boolean[]?': boolean[] | undefined;\n 'unknown[]': unknown[];\n 'unknown[]?': unknown[] | undefined;\n};\n\n/** Checks if a schema field type is optional (ends with ?). */\ntype IsOptionalField<T> = T extends `${string}?`\n ? true\n : T extends readonly [`${string}?`, string]\n ? true\n : false;\n\n/** Infers TypeScript type from a schema field (type string, tuple, or nested object). */\nexport type InferSchemaField<T> = T extends SchemaType\n ? SchemaTypeMap[T]\n : T extends readonly [infer S extends SchemaType, string]\n ? SchemaTypeMap[S]\n : T extends SchemaDefinition\n ? InferSchema<T>\n : never;\n\n/** Gets keys of required fields (non-optional types). */\ntype RequiredKeys<T extends SchemaDefinition> = {\n [K in keyof T]: IsOptionalField<T[K]> extends true ? never : K;\n}[keyof T];\n\n/** Gets keys of optional fields (types ending with ?). */\ntype OptionalKeys<T extends SchemaDefinition> = {\n [K in keyof T]: IsOptionalField<T[K]> extends true ? K : never;\n}[keyof T];\n\n/**\n * Infers TypeScript type from schema definition.\n *\n * Required fields become required properties, optional fields (ending with ?)\n * become optional properties with `| undefined`.\n */\nexport type InferSchema<T extends SchemaDefinition> = {\n [K in RequiredKeys<T>]: InferSchemaField<T[K]>;\n} & {\n [K in OptionalKeys<T>]?: InferSchemaField<T[K]>;\n};\n\n/** Error object returned when validation fails. */\nexport interface ValidationError {\n flatten: () => { fieldErrors: Record<string, string[]> };\n}\n\n/** Validation result matching Zod's safeParse API for compatibility. */\nexport type ValidationResult<T> =\n | { success: true; data: T; error?: undefined }\n | { success: false; data?: undefined; error: ValidationError };\n\n/** Compiled schema with validate method. */\nexport interface CompiledSchema<T> {\n safeParse: (data: object) => ValidationResult<T>;\n}\n\n/** Valid base types for primitives and arrays. */\nconst PRIMITIVE_TYPES = ['string', 'number', 'boolean', 'unknown'];\n\n/** Checks if a schema field is a nested object (not a type string or [type, description] tuple). */\nexport function isNestedSchema(field: SchemaField | SchemaDefinition): field is SchemaDefinition {\n return typeof field === 'object' && field !== null && !Array.isArray(field);\n}\n\n/** Unpacks a schema field into its type string and optional description. */\nexport function unpackField(field: SchemaField): [type: SchemaType, description?: string] {\n return Array.isArray(field) ? [field[0], field[1]] : [field as SchemaType, undefined];\n}\n\n/**\n * Validates and coerces a single primitive value.\n * Returns [success, result, errorMessage].\n */\nfunction validatePrimitive(value: unknown, baseType: string): [boolean, unknown, string | null] {\n switch (baseType) {\n case 'string':\n if (typeof value !== 'string') {\n return [false, undefined, 'Expected string'];\n }\n return [true, value, null];\n\n case 'number': {\n if (\n (typeof value !== 'number' && typeof value !== 'string') ||\n (typeof value === 'string' && value.trim() === '')\n ) {\n return [false, undefined, 'Expected number'];\n }\n const num = Number(value);\n if (!Number.isFinite(num)) {\n return [false, undefined, 'Expected number'];\n }\n return [true, num, null];\n }\n\n case 'boolean': {\n if (value === 'true' || value === '1' || value === true) {\n return [true, true, null];\n } else if (value === 'false' || value === '0' || value === false) {\n return [true, false, null];\n }\n return [false, undefined, 'Expected boolean (true/false)'];\n }\n\n case 'unknown':\n // Pass through any value without validation.\n return [true, value, null];\n\n default:\n return [false, undefined, `Unknown type: ${baseType}`];\n }\n}\n\n/**\n * Validates a schema field value (primitive, array, or nested object).\n * Returns [success, result, errors].\n */\nfunction validateField(\n key: string,\n field: SchemaField | SchemaDefinition,\n value: unknown,\n parentPath: string = '',\n): [boolean, unknown, Record<string, string[]>] {\n const fieldPath = parentPath ? `${parentPath}.${key}` : key;\n const errors: Record<string, string[]> = {};\n\n // Handle nested object schemas.\n if (isNestedSchema(field)) {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n errors[fieldPath] = ['Expected object'];\n return [false, undefined, errors];\n }\n // Recursively validate nested schema.\n const nestedResult = validateSchema(field, value as Record<string, unknown>, fieldPath);\n return [nestedResult.success, nestedResult.data, nestedResult.errors];\n }\n\n // Extract type string from field or [type, description] tuple.\n const [typeStr] = unpackField(field);\n const isOptional = typeStr.endsWith('?');\n const isArray = typeStr.includes('[]');\n\n // Strip optional marker for base type extraction.\n let baseType = isOptional ? typeStr.slice(0, -1) : typeStr;\n\n // Strip array marker.\n if (isArray) {\n baseType = baseType.replace('[]', '');\n }\n\n // Handle missing/empty values.\n if (value === undefined || value === null || value === '') {\n if (!isOptional) {\n errors[fieldPath] = ['Required'];\n return [false, undefined, errors];\n }\n return [true, undefined, {}];\n }\n\n // Handle array types.\n if (isArray) {\n if (!Array.isArray(value)) {\n errors[fieldPath] = ['Expected array'];\n return [false, undefined, errors];\n }\n\n const resultArray: unknown[] = [];\n for (let i = 0; i < value.length; i++) {\n const [success, coerced, errorMsg] = validatePrimitive(value[i], baseType);\n if (!success) {\n errors[`${fieldPath}[${i}]`] = [errorMsg!];\n return [false, undefined, errors];\n }\n resultArray.push(coerced);\n }\n return [true, resultArray, {}];\n }\n\n // Handle primitive types.\n const [success, coerced, errorMsg] = validatePrimitive(value, baseType);\n if (!success) {\n errors[fieldPath] = [errorMsg!];\n return [false, undefined, errors];\n }\n return [true, coerced, {}];\n}\n\n/**\n * Validates a full schema against data.\n * Returns { success, data, errors }.\n */\nfunction validateSchema(\n schema: SchemaDefinition,\n data: Record<string, unknown>,\n parentPath: string = '',\n): { success: boolean; data: Record<string, unknown>; errors: Record<string, string[]> } {\n const result: Record<string, unknown> = {};\n const allErrors: Record<string, string[]> = {};\n const source = typeof data === 'object' && data !== null && !Array.isArray(data) ? data : {};\n\n for (const [key, field] of Object.entries(schema)) {\n const value = Object.hasOwn(source, key) ? source[key] : undefined;\n const [success, coerced, fieldErrors] = validateField(key, field, value, parentPath);\n\n if (!success) {\n Object.assign(allErrors, fieldErrors);\n } else if (coerced !== undefined) {\n result[key] = coerced;\n }\n }\n\n return {\n success: Object.keys(allErrors).length === 0,\n data: result,\n errors: allErrors,\n };\n}\n\n/** Validates schema definition at compile time. */\nfunction validateSchemaDefinition(schema: SchemaDefinition, path: string = ''): void {\n for (const [key, field] of Object.entries(schema)) {\n const fieldPath = path ? `${path}.${key}` : key;\n\n if (isNestedSchema(field)) {\n validateSchemaDefinition(field, fieldPath);\n } else {\n if (\n Array.isArray(field) &&\n (field.length !== 2 || typeof field[0] !== 'string' || typeof field[1] !== 'string')\n ) {\n throw new Error(`Invalid field definition at '${fieldPath}'`);\n }\n const [typeStr] = unpackField(field);\n if (typeof typeStr !== 'string') {\n throw new Error(`Unknown type '${String(typeStr)}' at '${fieldPath}'`);\n }\n const isOptional = typeStr.endsWith('?');\n let baseType = isOptional ? typeStr.slice(0, -1) : typeStr;\n baseType = baseType.replace('[]', '');\n\n if (!PRIMITIVE_TYPES.includes(baseType)) {\n throw new Error(`Unknown type '${typeStr}' at '${fieldPath}'`);\n }\n }\n }\n}\n\n/**\n * Compiles a schema definition into a validator.\n *\n * Used internally by the router to validate query parameters and request bodies.\n * Provides a Zod-compatible `safeParse` API for consistency.\n *\n * @param schema - The schema definition object\n * @returns A compiled schema with `safeParse` method\n *\n * @example\n * ```typescript\n * const userSchema = compileSchema({\n * name: 'string',\n * age: 'number',\n * email: 'string?',\n * });\n *\n * const result = userSchema.safeParse({ name: 'Alice', age: 30 });\n * if (result.success) {\n * console.log(result.data.name); // 'Alice'\n * } else {\n * console.log(result.error.flatten().fieldErrors);\n * }\n * ```\n */\nexport function compileSchema<const T extends SchemaDefinition>(\n schema: T,\n): CompiledSchema<InferSchema<T>> {\n // Validate schema structure at compile time.\n validateSchemaDefinition(schema);\n\n return {\n safeParse(data: object): ValidationResult<InferSchema<T>> {\n const {\n success,\n data: result,\n errors,\n } = validateSchema(schema, data as Record<string, unknown>);\n\n if (!success) {\n return {\n success: false,\n error: { flatten: () => ({ fieldErrors: errors }) },\n };\n }\n\n return { success: true, data: result as InferSchema<T> };\n },\n };\n}\n\n/** JSON Schema object produced by {@link toJsonSchema}. */\nexport interface JsonSchemaObject {\n type: 'object';\n properties: Record<string, unknown>;\n /** Omitted when empty, as OpenAPI 3.0 requires. */\n required?: string[];\n}\n\n/**\n * Converts a schema definition to JSON Schema, recursing into nested objects.\n */\nexport function toJsonSchema(schema: SchemaDefinition): JsonSchemaObject {\n const properties: Record<string, unknown> = {};\n const required: string[] = [];\n for (const [key, field] of Object.entries(schema)) {\n if (isNestedSchema(field)) {\n properties[key] = toJsonSchema(field);\n required.push(key);\n continue;\n }\n const [typeStr, description] = unpackField(field);\n const described = description === undefined ? {} : { description };\n const optional = typeStr.endsWith('?');\n const base = optional ? typeStr.slice(0, -1) : typeStr;\n const primitive = base.replace('[]', '');\n const item = primitive === 'unknown' ? {} : { type: primitive };\n properties[key] = base.endsWith('[]')\n ? { type: 'array', items: item, ...described }\n : { ...item, ...described };\n if (!optional) required.push(key);\n }\n return { type: 'object', properties, ...(required.length > 0 && { required }) };\n}\n","/**\n * @fileoverview Core type definitions for the routing library.\n *\n * **Path Convention:**\n * - Property names = URL path segments\n * - `$param` prefix = dynamic segment (`:param`)\n * - `get`, `post`, `put`, `patch`, `delete` = HTTP method handlers\n *\n * **Structure:**\n * - `router({ ... })` - groups paths, can have middleware\n * - `route({ query?, body?, handler })` - single method with schemas\n * - Bare functions as shorthand for handlers without schemas\n */\n\nimport type { Middleware } from './middleware.js';\nimport type { InferSchema, SchemaDefinition } from './schema.js';\n\n/** Standard HTTP methods. */\nexport type Method = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'options' | 'head';\n\n/** HTTP method property names. */\nexport const HTTP_METHODS = new Set<string>([\n 'get',\n 'post',\n 'put',\n 'patch',\n 'delete',\n 'options',\n 'head',\n]);\n\n/** Symbol to mark an object as a route definition. */\nexport const ROUTE_MARKER = Symbol.for('@fresho/router:route');\n\n/** Symbol to mark an object as a router. */\nexport const ROUTER_MARKER = Symbol.for('@fresho/router:router');\n\n/**\n * Brand symbol for Router type identification across module boundaries.\n * This enables TypeScript to recognize Router types even when generic\n * type information is lost during cross-package imports.\n */\ndeclare const ROUTER_BRAND: unique symbol;\n\n/** Brand type for Router identification in conditional types. */\nexport type RouterBrand = { readonly [ROUTER_BRAND]: true };\n\n/**\n * Extracts the parameter name from a `$param` property name.\n */\nexport type ExtractParamFromProperty<T extends string> = T extends `$${infer Param}`\n ? Param\n : never;\n\n/**\n * Checks if a property name is a dynamic parameter (starts with $).\n */\nexport type IsParamProperty<T extends string> = T extends `$${string}` ? true : false;\n\n/**\n * Collects path params from a property path array.\n */\nexport type CollectPathParams<Path extends string[]> = Path extends [\n infer Head extends string,\n ...infer Rest extends string[],\n]\n ? (Head extends `$${infer Param}` ? { [K in Param]: string } : {}) & CollectPathParams<Rest>\n : {};\n\n/** Execution context for background tasks (Cloudflare Workers compatible). */\nexport interface ExecutionContext {\n waitUntil(promise: Promise<unknown>): void;\n passThroughOnException(): void;\n}\n\n/**\n * Unified context object passed to handlers.\n */\nexport type Context<\n Q = unknown,\n B = unknown,\n P extends Record<string, string> = Record<string, string>,\n Ctx = {},\n> = {\n /** The original Request. */\n request: Request;\n /** URL path parameters from `$param` segments. */\n path: P;\n /** Query string parameters. */\n query: Q;\n /** Request body. */\n body: B;\n /** Execution context for background tasks. */\n executionCtx?: ExecutionContext;\n /** Environment bindings. */\n env: Ctx extends { env: infer E } ? E : unknown;\n} & Omit<Ctx, 'env'>;\n\n/**\n * Typed response wrapper that carries the response body type.\n */\nexport type TypedResponse<T> = Response & { __responseType?: T };\n\n/**\n * Handler function type.\n */\nexport type TypedHandler<\n Q = unknown,\n B = unknown,\n P extends Record<string, string> = Record<string, string>,\n R = unknown,\n Ctx = {},\n> = (\n context: Context<Q, B, P, Ctx>,\n) => R | Response | TypedResponse<R> | Promise<R | Response | TypedResponse<R>>;\n\n/** MCP tool behavior hints; defaults come from the HTTP method. */\nexport interface ToolAnnotations {\n /** Human-readable tool title. */\n title?: string;\n /** The tool does not modify its environment. */\n readOnlyHint?: boolean;\n /** The tool may perform destructive updates. */\n destructiveHint?: boolean;\n /** Repeating the call with the same arguments has no additional effect. */\n idempotentHint?: boolean;\n /** The tool interacts with external entities. */\n openWorldHint?: boolean;\n}\n\n/**\n * Route definition - a single HTTP method handler with optional schemas.\n *\n * Use `route()` when you need query or body validation schemas.\n * For simple handlers without schemas, use bare functions in the router.\n *\n * @example\n * ```typescript\n * // With schemas\n * get: route({\n * query: { limit: 'number?' },\n * handler: async (c) => ({ items: [], limit: c.query.limit }),\n * })\n *\n * // Without schemas (bare function shorthand)\n * get: async (c) => ({ items: [] })\n * ```\n */\nexport interface RouteDefinition<\n Q extends SchemaDefinition = {},\n B extends SchemaDefinition = {},\n R = unknown,\n P extends Record<string, string> = Record<string, string>,\n Ctx = {},\n> {\n /** Query parameter schema for validation. */\n query?: Q;\n /** Request body schema for validation. */\n body?: B;\n /** Optional description for documentation. */\n description?: string;\n /** Operation name, used as the OpenAPI `operationId` and the MCP tool name. */\n operationId?: string;\n /** MCP tool settings. */\n mcp?: { annotations?: ToolAnnotations };\n /** The request handler function. */\n handler: TypedHandler<InferSchema<Q>, InferSchema<B>, P, R, Ctx>;\n}\n\n/** A bare handler function (shorthand for route without schemas). */\nexport type BareHandler = (\n context: Context<unknown, unknown, Record<string, string>, unknown>,\n) => unknown;\n\n/** Checks if a value is a function. */\nexport function isFunction(value: unknown): value is BareHandler {\n return typeof value === 'function';\n}\n\n/** Checks if entry is a Route (marked with ROUTE_MARKER). */\nexport function isRoute(entry: unknown): entry is RouteDefinition {\n return typeof entry === 'object' && entry !== null && ROUTE_MARKER in entry;\n}\n\n/** Checks if entry is a Router (marked with ROUTER_MARKER). */\nexport function isRouter(entry: unknown): entry is Router<RouterRoutes> {\n return typeof entry === 'object' && entry !== null && ROUTER_MARKER in entry;\n}\n\n/**\n * A method handler - either a route with schemas or a bare function.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type MethodEntry = RouteDefinition<any, any, any, any, any> | BareHandler;\n\n/**\n * Router routes record.\n *\n * Can contain:\n * - `get`, `post`, `put`, `patch`, `delete` - method handlers (route or function)\n * - Other property names - nested routers (path segments)\n * - `$param` properties - dynamic path segments\n */\nexport type RouterRoutes = {\n get?: MethodEntry;\n post?: MethodEntry;\n put?: MethodEntry;\n patch?: MethodEntry;\n delete?: MethodEntry;\n options?: MethodEntry;\n head?: MethodEntry;\n} & {\n [key: string]: MethodEntry | Router<RouterRoutes>;\n};\n\n/** Handler function signature for the standalone router. */\nexport type FetchHandler = (\n request: Request,\n env?: unknown,\n ctx?: ExecutionContext,\n) => Response | Promise<Response>;\n\n/**\n * Router - groups paths and can have middleware.\n *\n * @example\n * ```typescript\n * const api = router({\n * // Method handlers for this path\n * get: async (c) => [...],\n * post: route({ body: { name: 'string' }, handler: async (c) => c.body }),\n *\n * // Nested path segments\n * users: router({\n * get: async (c) => [...],\n * $id: router({\n * get: async (c) => ({ id: c.path.id }),\n * }),\n * }),\n * });\n * ```\n */\nexport interface Router<T extends RouterRoutes> extends RouterBrand {\n /** Route definitions and nested routers. */\n readonly routes: T;\n /** Middleware applied to all routes in this router. */\n readonly middleware?: Middleware[];\n /**\n * Creates a fetch handler for use with Cloudflare Workers, Deno, Bun, etc.\n */\n handler(): FetchHandler;\n}\n","/**\n * @fileoverview HTTP request handler for @fresho/router.\n *\n * Builds paths from property names:\n * - Regular properties = path segments\n * - `$param` properties = dynamic segments (`:param`)\n * - `get`, `post`, etc. = method handlers\n */\n\nimport type { Middleware, MiddlewareContext } from './middleware.js';\nimport { runMiddleware } from './middleware.js';\nimport type { CompiledSchema } from './schema.js';\nimport { compileSchema } from './schema.js';\nimport type {\n ExecutionContext,\n FetchHandler,\n RouteDefinition,\n Router,\n RouterRoutes,\n} from './types.js';\nimport { HTTP_METHODS, isFunction, isRoute, isRouter } from './types.js';\n\n/** Compiled route with pattern matcher and handler. */\ninterface CompiledRoute {\n method: string;\n pattern: RegExp;\n paramNames: string[];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n handler: (context: any) => unknown;\n middleware: Middleware[];\n querySchema?: CompiledSchema<unknown>;\n bodySchema?: CompiledSchema<unknown>;\n}\n\n/**\n * Escapes special regex characters in a string.\n */\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Converts a path with :param segments to a regex pattern.\n */\nfunction pathToRegex(path: string): { pattern: RegExp; paramNames: string[] } {\n const paramNames: string[] = [];\n const parts = path.split(/:(\\w+)/g);\n let regexStr = '';\n\n for (let i = 0; i < parts.length; i++) {\n if (i % 2 === 0) {\n regexStr += escapeRegex(parts[i]);\n } else {\n paramNames.push(parts[i]);\n regexStr += '([^/]+)';\n }\n }\n\n return {\n pattern: new RegExp(`^${regexStr}$`),\n paramNames,\n };\n}\n\n/**\n * Converts a property name to a path segment.\n * - `$param` becomes `:param`\n * - Other names become literal segments\n */\nfunction propertyToSegment(prop: string): string {\n if (prop.startsWith('$')) {\n return `:${prop.slice(1)}`;\n }\n return prop;\n}\n\n/** Collects and compiles all routes from a router tree. */\nfunction compileRoutes(\n routerDef: Router<RouterRoutes>,\n parentPath = '',\n parentMiddleware: Middleware[] = [],\n): CompiledRoute[] {\n const currentMiddleware = [...parentMiddleware, ...(routerDef.middleware || [])];\n const compiled: CompiledRoute[] = [];\n\n for (const [prop, entry] of Object.entries(routerDef.routes)) {\n // Check if entry is a nested router first (takes precedence over method name check).\n // This allows path segments named 'get', 'post', etc. when they contain routers.\n if (isRouter(entry)) {\n // Nested router - recurse with updated path.\n const segment = propertyToSegment(prop);\n const newPath = parentPath ? `${parentPath}/${segment}` : `/${segment}`;\n compiled.push(...compileRoutes(entry, newPath, currentMiddleware));\n } else if (HTTP_METHODS.has(prop)) {\n // Method handler (get, post, etc.) - only when entry is a route or function.\n const method = prop.toUpperCase();\n const path = parentPath || '/';\n const { pattern, paramNames } = pathToRegex(path);\n\n if (isFunction(entry)) {\n // Bare function handler.\n compiled.push({\n method,\n pattern,\n paramNames,\n handler: entry,\n middleware: currentMiddleware,\n });\n } else if (isRoute(entry)) {\n // Route with schemas.\n const routeDef = entry as RouteDefinition;\n compiled.push({\n method,\n pattern,\n paramNames,\n handler: routeDef.handler,\n middleware: currentMiddleware,\n querySchema: routeDef.query ? compileSchema(routeDef.query) : undefined,\n bodySchema: routeDef.body ? compileSchema(routeDef.body) : undefined,\n });\n }\n }\n }\n\n return compiled;\n}\n\n/**\n * Strips the body from a response for HEAD requests.\n */\nfunction stripBodyForHead(response: Response, isHead: boolean): Response {\n if (!isHead) return response;\n return new Response(null, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n}\n\n/**\n * Creates a fetch handler from a router definition.\n *\n * @example\n * ```typescript\n * const api = router({\n * health: router({\n * get: async () => ({ status: 'ok' }),\n * }),\n * users: router({\n * get: async () => [...],\n * $id: router({\n * get: async (c) => ({ id: c.path.id }),\n * }),\n * }),\n * });\n *\n * export default { fetch: api.handler() };\n * ```\n */\nexport function createHandler<T extends RouterRoutes>(routerDef: Router<T>): FetchHandler {\n const compiledRoutes = compileRoutes(routerDef);\n\n return async (request: Request, env?: unknown, ctx?: ExecutionContext): Promise<Response> => {\n const url = new URL(request.url);\n const method = request.method.toUpperCase();\n const isHead = method === 'HEAD';\n\n // Find matching route.\n for (const compiled of compiledRoutes) {\n const methodMatches =\n compiled.method === method ||\n method === 'OPTIONS' ||\n (method === 'HEAD' && compiled.method === 'GET');\n if (!methodMatches) continue;\n\n const match = compiled.pattern.exec(url.pathname);\n if (!match) continue;\n\n // Extract path parameters.\n const path: Record<string, string> = {};\n for (let i = 0; i < compiled.paramNames.length; i++) {\n path[compiled.paramNames[i]] = decodeURIComponent(match[i + 1]);\n }\n\n // Create middleware context.\n const context: MiddlewareContext = {\n request,\n path,\n query: {},\n body: {},\n env,\n executionCtx: ctx,\n };\n\n // Run middleware and handler.\n if (\n compiled.middleware.length > 0 ||\n compiled.handler ||\n compiled.querySchema ||\n compiled.bodySchema\n ) {\n const response = await runMiddleware(compiled.middleware, context, async () => {\n const routeId = `${compiled.method} ${url.pathname}`;\n\n // Validate query params.\n let query: unknown = {};\n if (compiled.querySchema) {\n const raw = Object.fromEntries(url.searchParams);\n const result = compiled.querySchema.safeParse(raw);\n if (!result.success) {\n return Response.json(\n {\n error: 'Invalid query parameters',\n route: routeId,\n details: result.error.flatten(),\n },\n { status: 400 },\n );\n }\n query = result.data;\n }\n\n // Validate body.\n let body: unknown = {};\n if (compiled.bodySchema && ['POST', 'PUT', 'PATCH'].includes(method)) {\n const raw = await request.json().catch(() => ({}));\n const result = compiled.bodySchema.safeParse(raw);\n if (!result.success) {\n return Response.json(\n { error: 'Invalid request body', route: routeId, details: result.error.flatten() },\n { status: 400 },\n );\n }\n body = result.data;\n }\n\n context.query = query;\n context.body = body;\n\n if (compiled.handler) {\n // Build handler context with middleware extensions.\n const handlerContext = {\n request,\n path,\n query,\n body,\n env,\n executionCtx: ctx,\n };\n\n // Copy middleware extensions.\n for (const key of Object.keys(context)) {\n if (!['request', 'path', 'query', 'body', 'env', 'executionCtx'].includes(key)) {\n (handlerContext as Record<string, unknown>)[key] = (\n context as Record<string, unknown>\n )[key];\n }\n }\n\n const result = await compiled.handler(handlerContext);\n if (result instanceof Response) {\n return result;\n }\n return Response.json(result);\n }\n\n return Response.json({});\n });\n return stripBodyForHead(response, isHead);\n }\n\n return stripBodyForHead(Response.json({}), isHead);\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n };\n}\n","/**\n * @fileoverview Core route and router creation functions.\n *\n * ## Path Convention\n * - Property names = URL path segments\n * - `$param` prefix = dynamic segment (`:param`)\n * - `get`, `post`, `put`, `patch`, `delete` = HTTP method handlers\n *\n * ## Typing Best Practices\n *\n * **Schemas** (`query`/`body`) provide runtime validation AND type inference.\n * Use them for any input that needs validation:\n * ```typescript\n * get: route({\n * query: { limit: 'number?' },\n * handler: async (c) => c.query.limit, // limit: number | undefined\n * })\n * ```\n *\n * **Context** (`route.ctx<T>()`) provides types only (no validation).\n * Use it for path params, env bindings, and middleware-injected values:\n * ```typescript\n * interface MyContext {\n * path: { id: string };\n * env: { DB: Database };\n * user: { name: string }; // from auth middleware\n * }\n * get: route.ctx<MyContext>()({\n * handler: async (c) => ({ id: c.path.id, user: c.user.name }),\n * })\n * ```\n *\n * **Important:** Don't add explicit type annotations to handler parameters.\n * Let types flow from schemas and context:\n * ```typescript\n * // GOOD: types inferred from schema\n * handler: async (c) => c.query.limit\n *\n * // BAD: redundant type annotation\n * handler: async (c: { query: { limit?: number } }) => c.query.limit\n * ```\n */\n\nimport { createHandler } from './handler.js';\nimport type { Middleware } from './middleware.js';\nimport type { SchemaDefinition } from './schema.js';\nimport type { RouteDefinition, Router, RouterRoutes } from './types.js';\nimport { ROUTE_MARKER, ROUTER_MARKER } from './types.js';\n\n/**\n * Creates a route with query/body validation schemas.\n *\n * Use `route()` when you need request validation. For simple handlers\n * without validation, you can use bare functions directly in the router.\n *\n * @example\n * ```typescript\n * // Route with query validation\n * get: route({\n * query: { limit: 'number?', offset: 'number?' },\n * handler: async (c) => {\n * const items = await db.list(c.query.limit, c.query.offset);\n * return { items };\n * },\n * })\n *\n * // Route with body validation\n * post: route({\n * body: { name: 'string', email: 'string' },\n * handler: async (c) => {\n * const user = await db.create(c.body);\n * return user;\n * },\n * })\n *\n * // Simple handler without validation (bare function)\n * get: async () => ({ status: 'ok' })\n * ```\n */\nexport function route<\n const Q extends SchemaDefinition = {},\n const B extends SchemaDefinition = {},\n R = unknown,\n>(definition: RouteDefinition<Q, B, R, {}, {}>): RouteDefinition<Q, B, R, {}, {}> {\n return { ...definition, [ROUTE_MARKER]: true } as RouteDefinition<Q, B, R, {}, {}>;\n}\n\n/** Return type of route.ctx<T>() - callable and chainable. */\ninterface CtxBuilder<Ctx> {\n <const Q extends SchemaDefinition = {}, const B extends SchemaDefinition = {}, R = unknown>(\n definition: RouteDefinition<Q, B, R, {}, Ctx>,\n ): RouteDefinition<Q, B, R, {}, Ctx>;\n\n ctx<AdditionalCtx>(): CtxBuilder<Ctx & AdditionalCtx>;\n}\n\n/** Creates a chainable context builder. */\nfunction createCtxBuilder<Ctx>(): CtxBuilder<Ctx> {\n const builder = <\n const Q extends SchemaDefinition = {},\n const B extends SchemaDefinition = {},\n R = unknown,\n >(\n definition: RouteDefinition<Q, B, R, {}, Ctx>,\n ): RouteDefinition<Q, B, R, {}, Ctx> =>\n ({ ...definition, [ROUTE_MARKER]: true }) as RouteDefinition<Q, B, R, {}, Ctx>;\n\n builder.ctx = <AdditionalCtx>() => createCtxBuilder<Ctx & AdditionalCtx>();\n\n return builder as CtxBuilder<Ctx>;\n}\n\n/**\n * Creates a route with typed context for values not covered by schemas.\n *\n * Use `route.ctx<T>()` to type:\n * - **Path params**: `{ path: { id: string } }` for `$id` segments\n * - **Env bindings**: `{ env: { DB: Database } }` for runtime environment\n * - **Middleware values**: `{ user: User }` for auth middleware, etc.\n *\n * Context provides types only, not runtime validation. For validated inputs,\n * use `query` and `body` schemas instead.\n *\n * @example\n * ```typescript\n * interface AppContext {\n * path: { id: string };\n * env: { DB: D1Database };\n * user: { id: string }; // injected by auth middleware\n * }\n *\n * $id: router({\n * get: route.ctx<AppContext>()({\n * query: { include: 'string?' }, // schema for validation\n * handler: async (c) => {\n * // c.path.id, c.env.DB, c.user.id - from context\n * // c.query.include - from schema\n * return c.env.DB.get(c.path.id);\n * },\n * }),\n * })\n * ```\n */\nroute.ctx = <Ctx>(): CtxBuilder<Ctx> => createCtxBuilder<Ctx>();\n\n/**\n * Creates a router that groups paths and applies middleware.\n *\n * @example\n * ```typescript\n * const api = router({\n * health: router({\n * get: async () => ({ status: 'ok' }),\n * }),\n *\n * users: router({\n * // GET /users - list users\n * get: route({\n * query: { limit: 'number?' },\n * handler: async (c) => db.users.list(c.query.limit),\n * }),\n *\n * // POST /users - create user\n * post: route({\n * body: { name: 'string', email: 'string' },\n * handler: async (c) => db.users.create(c.body),\n * }),\n *\n * // /users/:id\n * $id: router({\n * get: async (c) => db.users.get(c.path.id),\n * delete: async (c) => db.users.delete(c.path.id),\n * }),\n * }),\n * });\n *\n * export default { fetch: api.handler() };\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function router<T extends RouterRoutes, M extends Middleware<any>[]>(\n routes: T,\n ...middleware: M\n): Router<T> {\n // Cast needed because RouterBrand uses a type-only symbol declaration.\n const self = {\n routes,\n middleware: middleware.length > 0 ? middleware : undefined,\n handler() {\n return createHandler(self);\n },\n } as unknown as Router<T>;\n // Add marker for runtime type checking.\n (self as unknown as Record<symbol, boolean>)[ROUTER_MARKER] = true;\n return self;\n}\n","/**\n * @fileoverview OpenAPI documentation generation.\n *\n * Generates OpenAPI 3.0 documentation from router definitions.\n *\n * **Path Convention:**\n * - Property names = URL path segments\n * - `$param` prefix = dynamic segment (`{param}` in OpenAPI)\n * - `get`, `post`, `put`, `patch`, `delete` = HTTP method handlers\n */\n\nimport {\n isNestedSchema,\n type SchemaDefinition,\n type SchemaField,\n toJsonSchema,\n unpackField,\n} from './schema.js';\nimport type { RouteDefinition, Router, RouterRoutes } from './types.js';\nimport { HTTP_METHODS, isFunction, isRoute, isRouter } from './types.js';\n\n/** Collected route info for documentation. */\nexport interface CollectedRoute {\n path: string;\n method: string;\n route: RouteDefinition | null;\n}\n\n/** Converts property name to path segment, handling $param convention. */\nfunction propertyToSegment(prop: string): string {\n if (prop.startsWith('$')) {\n return `{${prop.slice(1)}}`;\n }\n return prop;\n}\n\n/** Collects all routes from a router tree, with `{param}` path segments. */\nexport function collectRoutes(routerDef: Router<RouterRoutes>, parentPath = ''): CollectedRoute[] {\n const collectedRoutes: CollectedRoute[] = [];\n\n for (const [prop, entry] of Object.entries(routerDef.routes)) {\n // Check if this is a method handler.\n if (HTTP_METHODS.has(prop)) {\n const path = parentPath || '/';\n if (isFunction(entry)) {\n collectedRoutes.push({ path, method: prop, route: null });\n } else if (isRoute(entry)) {\n collectedRoutes.push({ path, method: prop, route: entry });\n }\n } else if (isRouter(entry)) {\n // Nested router - recurse with updated path.\n const segment = propertyToSegment(prop);\n const newPath = parentPath ? `${parentPath}/${segment}` : `/${segment}`;\n collectedRoutes.push(...collectRoutes(entry, newPath));\n }\n }\n\n return collectedRoutes;\n}\n\n/** Generates OpenAPI documentation from a router. */\nexport function generateDocs(config: {\n title: string;\n version: string;\n description?: string;\n router: Router<RouterRoutes>;\n}): object {\n const paths: Record<string, Record<string, object>> = {};\n const allRoutes = collectRoutes(config.router);\n\n for (const { path, method, route } of allRoutes) {\n if (!paths[path]) {\n paths[path] = {};\n }\n\n const operation: Record<string, unknown> = {\n operationId: route?.operationId,\n description: route?.description,\n responses: {\n 200: { description: 'Successful response' },\n 400: { description: 'Validation error' },\n },\n };\n\n // Add query parameters (only primitive types supported in query strings).\n if (route?.query) {\n const querySchema = route.query as SchemaDefinition;\n operation.parameters = Object.entries(querySchema)\n .filter(([, field]) => !isNestedSchema(field))\n .map(([name, field]) => {\n const [type, description] = unpackField(field as SchemaField);\n return {\n name,\n in: 'query',\n required: !type.endsWith('?'),\n description,\n schema: { type: type.replace('?', '').replace('[]', '') },\n };\n });\n }\n\n // Add request body.\n if (route?.body && ['post', 'put', 'patch'].includes(method)) {\n operation.requestBody = {\n required: true,\n content: {\n 'application/json': {\n schema: toJsonSchema(route.body as SchemaDefinition),\n },\n },\n };\n }\n\n paths[path][method] = operation;\n }\n\n return {\n openapi: '3.0.0',\n info: {\n title: config.title,\n version: config.version,\n description: config.description,\n },\n paths,\n };\n}\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as InferSchema, D as
|
|
1
|
+
import { C as InferSchema, D as SchemaTypeMap, E as SchemaType, O as ValidationResult, S as CompiledSchema, T as SchemaField, _ as TypedHandler, a as runMiddleware, b as isRoute, c as ExecutionContext, d as MethodEntry, f as RouteDefinition, g as ToolAnnotations, h as RouterRoutes, k as compileSchema, l as FetchHandler, m as RouterBrand, n as MiddlewareContext, o as CollectPathParams, p as Router, r as MiddlewareNext, s as Context, t as Middleware, u as Method, v as TypedResponse, w as SchemaDefinition, x as isRouter, y as isFunction } from "./middleware-Cj1yHSJF.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/core.d.ts
|
|
4
4
|
|
|
@@ -411,5 +411,5 @@ declare function streamJsonLines(handler: (send: (data: object) => void, close:
|
|
|
411
411
|
headers?: HeadersInit;
|
|
412
412
|
}): Response;
|
|
413
413
|
//#endregion
|
|
414
|
-
export { type CollectPathParams, type CompiledSchema, type Context, type ExecutionContext, type FetchHandler, type HttpClient, type HttpClientConfig, type HttpRequestOptions, type InferSchema, type LocalClient, type LocalClientConfig, type LocalRequestOptions, type Method, type MethodEntry, type Middleware, type MiddlewareContext, type MiddlewareNext, type RouteDefinition, type Router, type RouterBrand, type RouterRoutes, type SSEData, type SSEEventMap, type SSEEventMessage, type SSEEventsOf, type SSEMessage, type SSEOptions, type SSEResponse, type SchemaDefinition, type SchemaType, type SchemaTypeMap, type ToolAnnotations, type TypedHandler, type TypedResponse, type ValidationResult, compileSchema, createHttpClient, createLocalClient, generateDocs, isFunction, isRoute, isRouter, route, router, runMiddleware, sseResponse, streamJsonLines };
|
|
414
|
+
export { type CollectPathParams, type CompiledSchema, type Context, type ExecutionContext, type FetchHandler, type HttpClient, type HttpClientConfig, type HttpRequestOptions, type InferSchema, type LocalClient, type LocalClientConfig, type LocalRequestOptions, type Method, type MethodEntry, type Middleware, type MiddlewareContext, type MiddlewareNext, type RouteDefinition, type Router, type RouterBrand, type RouterRoutes, type SSEData, type SSEEventMap, type SSEEventMessage, type SSEEventsOf, type SSEMessage, type SSEOptions, type SSEResponse, type SchemaDefinition, type SchemaField, type SchemaType, type SchemaTypeMap, type ToolAnnotations, type TypedHandler, type TypedResponse, type ValidationResult, compileSchema, createHttpClient, createLocalClient, generateDocs, isFunction, isRoute, isRouter, route, router, runMiddleware, sseResponse, streamJsonLines };
|
|
415
415
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/core.ts","../src/docs.ts","../src/client-types.ts","../src/http-client.ts","../src/local-client.ts","../src/streaming.ts"],"sourcesContent":[],"mappings":";;;;;;;AEQA;AAGA;;;;;;;;AAaA;;;;;;;AAOA;AAGA;;;;;AAIA;;;;;AAKS,iBFoCO,KEpCP,CAAA,kBFqCS,gBErCT,GAAA,CAAA,CAAA,EAAA,kBFsCS,gBEtCT,GAAA,CAAA,CAAA,EAAA,MAAA,OAAA,CAAA,CAAA,UAAA,EFwCK,eExCL,CFwCqB,GExCrB,EFwCwB,GExCxB,EFwC2B,GExC3B,EAAA,CAAA,CAAA,EAAA,CAAA,CAAA,CAAA,CAAA,EFwCwC,eExCxC,CFwCwD,GExCxD,EFwC2D,GExC3D,EFwC8D,GExC9D,EAAA,CAAA,CAAA,EAAA,CAAA,CAAA,CAAA;AACa,kBFmCN,KAAA,CEnCM;EAAZ,IAAA,GAAA,EAAA,CAAA,GAAA,CAAA,GAAA,GFmGW,UEnGX,CFmGsB,GEnGtB,CAAA;;AAIV;UFwCU,UExCqC,CAAA,GAAA,CAAA,CAAA;EAI3C,CAAA,kBFqCe,gBErCf,GAAA,CAAA,CAAA,EAAA,kBFqCsD,gBErCtD,GAAA,CAAA,CAAA,EAAA,MAAA,OAAA,CAAA,CAAA,UAAA,EFsCY,eEtCZ,CFsC4B,GEtC5B,EFsC+B,GEtC/B,EFsCkC,GEtClC,EAAA,CAAA,CAAA,EFsCyC,GEtCzC,CAAA,CAAA,EFuCC,eEvCD,CFuCiB,GEvCjB,EFuCoB,GEvCpB,EFuCuB,GEvCvB,EAAA,CAAA,CAAA,EFuC8B,GEvC9B,CAAA;EAEY,GAAA,CAAA,aAAA,CAAA,EAAA,EFuCQ,UEvCR,CFuCmB,GEvCnB,GFuCyB,aEvCzB,CAAA;;;AAIhB;;;;;;;AAQA;;;;;;;;AAKA;AASA;AAOA;AAGA;;;;;;;;;;;;;;;AAKwB,iBFqFR,MErFQ,CAAA,UFqFS,YErFT,EAAA,UFqFiC,UErFjC,CAAA,GAAA,CAAA,EAAA,CAAA,CAAA,MAAA,EFsFd,CEtFc,EAAA,GAAA,UAAA,EFuFP,CEvFO,CAAA,EFwFrB,MExFqB,CFwFd,CExFc,CAAA;;;;AFLW,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/core.ts","../src/docs.ts","../src/client-types.ts","../src/http-client.ts","../src/local-client.ts","../src/streaming.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as runMiddleware } from "./middleware-B-_0PLA6.mjs";
|
|
2
|
-
import { a as isFunction, c as compileSchema, i as router, n as generateDocs, o as isRoute, r as route, s as isRouter } from "./docs-
|
|
2
|
+
import { a as isFunction, c as compileSchema, i as router, n as generateDocs, o as isRoute, r as route, s as isRouter } from "./docs-C77XQzF3.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/client-proxy.ts
|
|
5
5
|
const PREFIXED_HTTP_METHODS = new Set([
|
package/dist/mcp/index.d.mts
CHANGED
package/dist/mcp/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/mcp/index.ts"],"sourcesContent":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/mcp/index.ts"],"sourcesContent":[],"mappings":";;;;;UAkDiB,eAAA;;;;;;;UAQA,UAAA;;;;;;;;UAQP;;;;;;;;;;;;;;;;;;;;;;;;;iBAkRM,GAAA,SACN,OAAO,gBAAgB,eAAe,OAAO,yBAC5C,2BACM,eACd,OAAO;;;;;;;;;;;;;;iBAyDM,sBAAA,QAA8B,kBAAkB,OAAO"}
|
package/dist/mcp/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as runMiddleware } from "../middleware-B-_0PLA6.mjs";
|
|
2
|
-
import { i as router, l as
|
|
2
|
+
import { d as unpackField, i as router, l as isNestedSchema, s as isRouter, t as collectRoutes, u as toJsonSchema } from "../docs-C77XQzF3.mjs";
|
|
3
3
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
4
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
5
5
|
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -97,7 +97,7 @@ function collectTools(root) {
|
|
|
97
97
|
const required = [];
|
|
98
98
|
const sources = /* @__PURE__ */ new Map();
|
|
99
99
|
const add = (source, schema) => {
|
|
100
|
-
const json = toJsonSchema(schema
|
|
100
|
+
const json = toJsonSchema(schema);
|
|
101
101
|
for (const [key, property] of Object.entries(json.properties)) {
|
|
102
102
|
if (sources.has(key)) throw new Error(`Duplicate argument ${key} in tool ${name}`);
|
|
103
103
|
sources.set(key, source);
|
|
@@ -108,7 +108,11 @@ function collectTools(root) {
|
|
|
108
108
|
const params = [...path.matchAll(/\{(\w+)\}/g)].map((m) => m[1]);
|
|
109
109
|
add("path", Object.fromEntries(params.map((p) => [p, "string"])));
|
|
110
110
|
if (route?.query) {
|
|
111
|
-
const scalars = Object.entries(route.query).filter(([,
|
|
111
|
+
const scalars = Object.entries(route.query).filter(([, field]) => {
|
|
112
|
+
if (isNestedSchema(field)) return false;
|
|
113
|
+
const [type] = unpackField(field);
|
|
114
|
+
return typeof type === "string" && !type.includes("[]");
|
|
115
|
+
});
|
|
112
116
|
add("query", Object.fromEntries(scalars));
|
|
113
117
|
}
|
|
114
118
|
const hasBodySchema = !!route?.body && METHODS_WITH_BODY.includes(method);
|
package/dist/mcp/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["DEFAULT_ANNOTATIONS: Record<string, ToolAnnotations>","properties: Record<string, object>","required: string[]","pathArgs: Record<string, unknown>","queryArgs: Record<string, unknown>","bodyArgs: Record<string, unknown>","missing: string | undefined","invalid: string | undefined","response: Response","result: CallToolResult","inherit: Middleware","dispatch: Dispatch"],"sources":["../../src/mcp/index.ts"],"sourcesContent":["/**\n * @fileoverview MCP server adapter for @fresho/router.\n *\n * Exposes routes as Model Context Protocol tools over stateless Streamable HTTP. Each tool call\n * is dispatched as a regular HTTP request through the router, so route middleware (auth, rate\n * limits, logging) applies to agents exactly as it does to other clients.\n *\n * Requires the optional peer dependency `@modelcontextprotocol/sdk`.\n *\n * @example\n * ```typescript\n * import { router } from '@fresho/router';\n * import { mcp } from '@fresho/router/mcp';\n * import { bearerAuth } from '@fresho/router/middleware';\n *\n * const api = router({ users, billing });\n *\n * export default {\n * fetch: router({\n * ...api.routes,\n * mcp: mcp({ users }, { name: 'my-api', version: '1.0.0' }, bearerAuth({ verify })),\n * }).handler(),\n * };\n * ```\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport {\n CallToolRequestSchema,\n type CallToolResult,\n ErrorCode,\n ListToolsRequestSchema,\n McpError,\n type Tool,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { router } from '../core.js';\nimport { collectRoutes } from '../docs.js';\nimport { type Middleware, type MiddlewareContext, runMiddleware } from '../middleware.js';\nimport { type SchemaDefinition, toJsonSchema } from '../schema.js';\nimport type { Context, Router, RouterRoutes, ToolAnnotations } from '../types.js';\nimport { isRouter } from '../types.js';\n\n/** OAuth 2.0 protected resource settings (RFC 9728). */\nexport interface McpOAuthOptions {\n /** Issuer URLs of the authorization servers that issue tokens for this server. */\n authorizationServers: string[];\n /** Scopes advertised to clients. */\n scopes?: string[];\n}\n\n/** MCP server options. */\nexport interface McpOptions {\n /** Server name reported to clients. */\n name: string;\n /** Server version reported to clients. */\n version: string;\n /** Instructions for agents on how to use the tools. */\n instructions?: string;\n /** Advertise OAuth: 401s gain a `resource_metadata` challenge and metadata is served. */\n oauth?: McpOAuthOptions;\n /** Allow an endpoint without middleware: it is open, or already guarded by parent routers. */\n public?: boolean;\n}\n\n/** Where a flat tool argument goes in the HTTP request. */\ntype ArgumentSource = 'path' | 'query' | 'body';\n\n/** A tool and the route it calls. */\ninterface ToolRoute {\n tool: Tool;\n method: string;\n path: string;\n sources: Map<string, ArgumentSource>;\n /** Without a body schema, arguments that aren't path or query fields form the body. */\n hasBodySchema: boolean;\n}\n\nconst METHODS_WITH_BODY = ['post', 'put', 'patch'];\nconst CONTEXT_KEYS = new Set(['request', 'path', 'query', 'body', 'env', 'executionCtx']);\n// Describe the outer MCP request or connection, not the dispatched tool call.\nconst DROPPED_HEADERS = [\n 'Connection',\n 'Content-Encoding',\n 'Content-Length',\n 'Content-Type',\n 'Keep-Alive',\n 'Mcp-Protocol-Version',\n 'Mcp-Session-Id',\n 'Proxy-Connection',\n 'TE',\n 'Trailer',\n 'Transfer-Encoding',\n 'Upgrade',\n];\nconst STREAMING_TYPES = /^(text\\/event-stream|application\\/x-ndjson)/i;\nconst TOOL_NAME = /^[A-Za-z0-9_-]{1,128}$/;\nconst DEFAULT_ANNOTATIONS: Record<string, ToolAnnotations> = {\n get: { readOnlyHint: true },\n put: { idempotentHint: true },\n delete: { destructiveHint: true, idempotentHint: true },\n};\nconst TOOL_METHODS = new Set(['get', 'delete', ...METHODS_WITH_BODY]);\nconst METADATA_PATH = '/.well-known/oauth-protected-resource';\n\n/** Parses JSON, returning undefined for invalid input. */\nfunction parseJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/** Builds a tool name such as `get_users_by_id` from a method and `{param}` path. */\nfunction toolName(method: string, path: string): string {\n const segments = path\n .split('/')\n .filter(Boolean)\n .map((s) => (s.startsWith('{') ? `by_${s.slice(1, -1)}` : s));\n return [method, ...segments].join('_').replace(/[^A-Za-z0-9_-]/g, '_');\n}\n\n/** Derives one tool per route in the tree, with path, query and body fields as flat arguments. */\nfunction collectTools(root: Router<RouterRoutes>): Map<string, ToolRoute> {\n const tools = new Map<string, ToolRoute>();\n for (const { path, method, route } of collectRoutes(root)) {\n if (!TOOL_METHODS.has(method)) continue;\n const name = route?.operationId ?? toolName(method, path);\n // MCP allows up to 128 characters; some clients reject other characters.\n if (!TOOL_NAME.test(name)) throw new Error(`Invalid tool name: ${name}`);\n if (tools.has(name)) throw new Error(`Duplicate tool name: ${name}`);\n\n const properties: Record<string, object> = {};\n const required: string[] = [];\n const sources = new Map<string, ArgumentSource>();\n const add = (source: ArgumentSource, schema: SchemaDefinition) => {\n const json = toJsonSchema(schema, route?.descriptions);\n for (const [key, property] of Object.entries(json.properties)) {\n if (sources.has(key)) throw new Error(`Duplicate argument ${key} in tool ${name}`);\n sources.set(key, source);\n properties[key] = property as object;\n }\n required.push(...(json.required ?? []));\n };\n const params = [...path.matchAll(/\\{(\\w+)\\}/g)].map((m) => m[1]);\n add('path', Object.fromEntries(params.map((p) => [p, 'string'])));\n if (route?.query) {\n // Query strings only carry scalars.\n const scalars = Object.entries(route.query as SchemaDefinition).filter(\n ([, type]) => typeof type === 'string' && !type.includes('[]'),\n );\n add('query', Object.fromEntries(scalars));\n }\n const hasBodySchema = !!route?.body && METHODS_WITH_BODY.includes(method);\n if (hasBodySchema) add('body', route?.body as SchemaDefinition);\n\n tools.set(name, {\n tool: {\n name,\n description: route?.description ?? `${method.toUpperCase()} ${path}`,\n inputSchema: { type: 'object', properties, ...(required.length > 0 && { required }) },\n annotations: { ...DEFAULT_ANNOTATIONS[method], ...route?.mcp?.annotations },\n },\n method,\n path,\n sources,\n hasBodySchema,\n });\n }\n return tools;\n}\n\n/** Sends a tool's request through the routes on behalf of the MCP request context `c`. */\ntype Dispatch = (request: Request, c: Context) => Response | Promise<Response>;\n\n/** Runs a tool as an HTTP request through the router, so its middleware applies. */\nasync function callTool(\n dispatch: Dispatch,\n target: ToolRoute,\n args: Record<string, unknown>,\n c: Context,\n): Promise<CallToolResult> {\n const fail = (text: string): CallToolResult => ({\n content: [{ type: 'text', text }],\n isError: true,\n });\n const pathArgs: Record<string, unknown> = {};\n const queryArgs: Record<string, unknown> = {};\n const bodyArgs: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(args)) {\n const source = target.sources.get(key) ?? (target.hasBodySchema ? undefined : 'body');\n if (source === 'path') pathArgs[key] = value;\n else if (source === 'query') queryArgs[key] = value;\n else if (source === 'body') bodyArgs[key] = value;\n }\n let missing: string | undefined;\n let invalid: string | undefined;\n const path = target.path.replace(/\\{(\\w+)\\}/g, (_, name: string) => {\n const value = pathArgs[name];\n if (value === undefined || value === null || value === '') missing ??= name;\n // URL parsing resolves dot segments, which would reach a different route.\n if (value === '.' || value === '..') invalid ??= name;\n return encodeURIComponent(String(value ?? ''));\n });\n if (missing) return fail(`Missing path parameter: ${missing}`);\n if (invalid) return fail(`Invalid path parameter: ${invalid}`);\n\n const url = new URL(path, c.request.url);\n for (const [key, value] of Object.entries(queryArgs)) {\n if (typeof value === 'object' && value !== null) return fail(`Invalid query parameter: ${key}`);\n if (value !== undefined && value !== null) url.searchParams.set(key, String(value));\n }\n\n // Forward the caller's headers (Authorization, cookies, ...) but describe the new request.\n const headers = new Headers(c.request.headers);\n for (const name of DROPPED_HEADERS) headers.delete(name);\n headers.set('Accept', 'application/json');\n const hasBody = METHODS_WITH_BODY.includes(target.method);\n if (hasBody) headers.set('Content-Type', 'application/json');\n\n let response: Response;\n try {\n response = await dispatch(\n new Request(url, {\n method: target.method.toUpperCase(),\n headers,\n body: hasBody ? JSON.stringify(bodyArgs) : undefined,\n }),\n c,\n );\n } catch (error) {\n console.error(`Error in MCP tool ${target.tool.name}:`, error);\n return fail('Internal error');\n }\n if (STREAMING_TYPES.test(response.headers.get('Content-Type') ?? '')) {\n await response.body?.cancel();\n return fail('Streaming responses are not supported');\n }\n const text = await response.text();\n const result: CallToolResult = { content: [{ type: 'text', text }], isError: !response.ok };\n if (/^application\\/json/i.test(response.headers.get('Content-Type') ?? '')) {\n const value = parseJson(text);\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n result.structuredContent = value as Record<string, unknown>;\n }\n }\n return result;\n}\n\n/** Handles one MCP request with a fresh stateless server. */\nasync function serve(\n tools: Map<string, ToolRoute>,\n dispatch: Dispatch,\n options: McpOptions,\n c: Context,\n): Promise<Response> {\n const server = new Server(\n { name: options.name, version: options.version },\n { capabilities: { tools: {} }, instructions: options.instructions },\n );\n server.setRequestHandler(ListToolsRequestSchema, () => ({\n tools: [...tools.values()].map((t) => t.tool),\n }));\n server.setRequestHandler(CallToolRequestSchema, (request) => {\n const target = tools.get(request.params.name);\n if (!target) {\n throw new McpError(ErrorCode.InvalidParams, `Unknown tool: ${request.params.name}`);\n }\n return callTool(dispatch, target, request.params.arguments ?? {}, c);\n });\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n return transport.handleRequest(c.request);\n}\n\n/** Adds the RFC 9728 `resource_metadata` parameter to a 401's Bearer challenge. */\nfunction challenge(oauth: McpOAuthOptions): Middleware {\n return async (c, next) => {\n const response = await next();\n if (response.status !== 401) return response;\n const url = new URL(c.request.url);\n const params = [`resource_metadata=\"${url.origin}${url.pathname}${METADATA_PATH}\"`];\n if (oauth.scopes?.length) params.push(`scope=\"${oauth.scopes.join(' ')}\"`);\n const existing = response.headers.get('WWW-Authenticate');\n const value = existing?.startsWith('Bearer ')\n ? `${existing}, ${params.join(', ')}`\n : `Bearer ${params.join(', ')}`;\n const challenged = new Response(response.body, response);\n challenged.headers.set('WWW-Authenticate', value);\n return challenged;\n };\n}\n\n/** Serves protected resource metadata for the resource named by the request path. */\nfunction metadata(oauth: McpOAuthOptions) {\n return (c: Context) => {\n const url = new URL(c.request.url);\n const resource = url.pathname.replace(METADATA_PATH, '');\n return Response.json({\n resource: `${url.origin}${resource}`,\n authorization_servers: oauth.authorizationServers,\n ...(oauth.scopes && { scopes_supported: oauth.scopes }),\n bearer_methods_supported: ['header'],\n });\n };\n}\n\n/**\n * Creates an MCP endpoint exposing routes as tools.\n *\n * Mount the result in a router (e.g. `mcp: mcp({ users }, options, auth)`). Tool names come from\n * `route.operationId` or the method and path (`get_users`, `delete_users_by_id`), descriptions\n * from `route.description`, and flat inputs from the route's path params and query/body schemas.\n *\n * Middleware passed after `options` guards the endpoint itself, and is required unless\n * `options.public` is set; put auth there so unauthenticated\n * clients get an HTTP 401 (with an OAuth challenge when `oauth` is set). Context it adds reaches\n * handlers. Middleware on the given routers runs on every tool call, with the caller's headers\n * forwarded; middleware on their parents elsewhere in the app does not.\n *\n * With `oauth`, metadata is served at `<endpoint>/.well-known/oauth-protected-resource` and\n * referenced from the 401 challenge. Mount {@link oauthProtectedResource} at the origin root as\n * well for clients that probe `/.well-known/oauth-protected-resource/<endpoint>` first.\n *\n * @param routes - A router, or a record of routers mounted under their keys\n * @param options - Server info and optional OAuth settings\n * @param middleware - Middleware for the MCP endpoint\n */\nexport function mcp(\n routes: Router<RouterRoutes> | Record<string, Router<RouterRoutes>>,\n options: McpOptions,\n ...middleware: Middleware[]\n): Router<RouterRoutes> {\n if (middleware.length === 0 && options.public !== true) {\n throw new Error('mcp() needs endpoint middleware such as auth, or public: true');\n }\n const base = isRouter(routes) ? routes : router(routes as RouterRoutes);\n const tools = collectTools(base);\n\n // Context set by middleware on the endpoint and its parents, keyed by dispatched request.\n const inherited = new WeakMap<Request, Record<string, unknown>>();\n const inherit: Middleware = (c, next) => {\n Object.assign(c, inherited.get(c.request));\n return next();\n };\n const fetchRoute = router(base.routes, inherit, ...(base.middleware ?? [])).handler();\n const dispatch: Dispatch = (request, c) => {\n inherited.set(\n request,\n Object.fromEntries(Object.entries(c).filter(([key]) => !CONTEXT_KEYS.has(key))),\n );\n return fetchRoute(request, c.env, c.executionCtx);\n };\n\n const chain = options.oauth ? [challenge(options.oauth), ...middleware] : middleware;\n const notAllowed = () => new Response(null, { status: 405, headers: { Allow: 'POST' } });\n\n return router({\n post: (c) => {\n // The MCP spec requires Origin validation against DNS rebinding.\n const origin = c.request.headers.get('Origin');\n if (origin && origin !== new URL(c.request.url).origin) {\n return new Response('Forbidden origin', { status: 403 });\n }\n return runMiddleware(chain, c as MiddlewareContext, () => serve(tools, dispatch, options, c));\n },\n get: notAllowed,\n delete: notAllowed,\n ...(options.oauth && {\n '.well-known': router({\n 'oauth-protected-resource': router({ get: metadata(options.oauth) }),\n }),\n }),\n });\n}\n\n/**\n * Creates RFC 9728 protected resource metadata routes to mount as `'.well-known'` at the origin\n * root, serving `/.well-known/oauth-protected-resource` and\n * `/.well-known/oauth-protected-resource/<resource>` for single-segment resource paths like `/mcp`.\n *\n * @example\n * ```typescript\n * router({\n * mcp: mcp({ users }, { name: 'api', version: '1.0.0', oauth }, jwtAuth({ ... })),\n * '.well-known': oauthProtectedResource(oauth),\n * });\n * ```\n */\nexport function oauthProtectedResource(oauth: McpOAuthOptions): Router<RouterRoutes> {\n const get = metadata(oauth);\n return router({ 'oauth-protected-resource': router({ get, $resource: router({ get }) }) });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["DEFAULT_ANNOTATIONS: Record<string, ToolAnnotations>","properties: Record<string, object>","required: string[]","pathArgs: Record<string, unknown>","queryArgs: Record<string, unknown>","bodyArgs: Record<string, unknown>","missing: string | undefined","invalid: string | undefined","response: Response","result: CallToolResult","inherit: Middleware","dispatch: Dispatch"],"sources":["../../src/mcp/index.ts"],"sourcesContent":["/**\n * @fileoverview MCP server adapter for @fresho/router.\n *\n * Exposes routes as Model Context Protocol tools over stateless Streamable HTTP. Each tool call\n * is dispatched as a regular HTTP request through the router, so route middleware (auth, rate\n * limits, logging) applies to agents exactly as it does to other clients.\n *\n * Requires the optional peer dependency `@modelcontextprotocol/sdk`.\n *\n * @example\n * ```typescript\n * import { router } from '@fresho/router';\n * import { mcp } from '@fresho/router/mcp';\n * import { bearerAuth } from '@fresho/router/middleware';\n *\n * const api = router({ users, billing });\n *\n * export default {\n * fetch: router({\n * ...api.routes,\n * mcp: mcp({ users }, { name: 'my-api', version: '1.0.0' }, bearerAuth({ verify })),\n * }).handler(),\n * };\n * ```\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport {\n CallToolRequestSchema,\n type CallToolResult,\n ErrorCode,\n ListToolsRequestSchema,\n McpError,\n type Tool,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { router } from '../core.js';\nimport { collectRoutes } from '../docs.js';\nimport { type Middleware, type MiddlewareContext, runMiddleware } from '../middleware.js';\nimport {\n isNestedSchema,\n type SchemaDefinition,\n type SchemaField,\n toJsonSchema,\n unpackField,\n} from '../schema.js';\nimport type { Context, Router, RouterRoutes, ToolAnnotations } from '../types.js';\nimport { isRouter } from '../types.js';\n\n/** OAuth 2.0 protected resource settings (RFC 9728). */\nexport interface McpOAuthOptions {\n /** Issuer URLs of the authorization servers that issue tokens for this server. */\n authorizationServers: string[];\n /** Scopes advertised to clients. */\n scopes?: string[];\n}\n\n/** MCP server options. */\nexport interface McpOptions {\n /** Server name reported to clients. */\n name: string;\n /** Server version reported to clients. */\n version: string;\n /** Instructions for agents on how to use the tools. */\n instructions?: string;\n /** Advertise OAuth: 401s gain a `resource_metadata` challenge and metadata is served. */\n oauth?: McpOAuthOptions;\n /** Allow an endpoint without middleware: it is open, or already guarded by parent routers. */\n public?: boolean;\n}\n\n/** Where a flat tool argument goes in the HTTP request. */\ntype ArgumentSource = 'path' | 'query' | 'body';\n\n/** A tool and the route it calls. */\ninterface ToolRoute {\n tool: Tool;\n method: string;\n path: string;\n sources: Map<string, ArgumentSource>;\n /** Without a body schema, arguments that aren't path or query fields form the body. */\n hasBodySchema: boolean;\n}\n\nconst METHODS_WITH_BODY = ['post', 'put', 'patch'];\nconst CONTEXT_KEYS = new Set(['request', 'path', 'query', 'body', 'env', 'executionCtx']);\n// Describe the outer MCP request or connection, not the dispatched tool call.\nconst DROPPED_HEADERS = [\n 'Connection',\n 'Content-Encoding',\n 'Content-Length',\n 'Content-Type',\n 'Keep-Alive',\n 'Mcp-Protocol-Version',\n 'Mcp-Session-Id',\n 'Proxy-Connection',\n 'TE',\n 'Trailer',\n 'Transfer-Encoding',\n 'Upgrade',\n];\nconst STREAMING_TYPES = /^(text\\/event-stream|application\\/x-ndjson)/i;\nconst TOOL_NAME = /^[A-Za-z0-9_-]{1,128}$/;\nconst DEFAULT_ANNOTATIONS: Record<string, ToolAnnotations> = {\n get: { readOnlyHint: true },\n put: { idempotentHint: true },\n delete: { destructiveHint: true, idempotentHint: true },\n};\nconst TOOL_METHODS = new Set(['get', 'delete', ...METHODS_WITH_BODY]);\nconst METADATA_PATH = '/.well-known/oauth-protected-resource';\n\n/** Parses JSON, returning undefined for invalid input. */\nfunction parseJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/** Builds a tool name such as `get_users_by_id` from a method and `{param}` path. */\nfunction toolName(method: string, path: string): string {\n const segments = path\n .split('/')\n .filter(Boolean)\n .map((s) => (s.startsWith('{') ? `by_${s.slice(1, -1)}` : s));\n return [method, ...segments].join('_').replace(/[^A-Za-z0-9_-]/g, '_');\n}\n\n/** Derives one tool per route in the tree, with path, query and body fields as flat arguments. */\nfunction collectTools(root: Router<RouterRoutes>): Map<string, ToolRoute> {\n const tools = new Map<string, ToolRoute>();\n for (const { path, method, route } of collectRoutes(root)) {\n if (!TOOL_METHODS.has(method)) continue;\n const name = route?.operationId ?? toolName(method, path);\n // MCP allows up to 128 characters; some clients reject other characters.\n if (!TOOL_NAME.test(name)) throw new Error(`Invalid tool name: ${name}`);\n if (tools.has(name)) throw new Error(`Duplicate tool name: ${name}`);\n\n const properties: Record<string, object> = {};\n const required: string[] = [];\n const sources = new Map<string, ArgumentSource>();\n const add = (source: ArgumentSource, schema: SchemaDefinition) => {\n const json = toJsonSchema(schema);\n for (const [key, property] of Object.entries(json.properties)) {\n if (sources.has(key)) throw new Error(`Duplicate argument ${key} in tool ${name}`);\n sources.set(key, source);\n properties[key] = property as object;\n }\n required.push(...(json.required ?? []));\n };\n const params = [...path.matchAll(/\\{(\\w+)\\}/g)].map((m) => m[1]);\n add('path', Object.fromEntries(params.map((p) => [p, 'string'])));\n if (route?.query) {\n // Query strings only carry scalars.\n const scalars = Object.entries(route.query as SchemaDefinition).filter(([, field]) => {\n if (isNestedSchema(field)) return false;\n const [type] = unpackField(field as SchemaField);\n return typeof type === 'string' && !type.includes('[]');\n });\n add('query', Object.fromEntries(scalars));\n }\n const hasBodySchema = !!route?.body && METHODS_WITH_BODY.includes(method);\n if (hasBodySchema) add('body', route?.body as SchemaDefinition);\n\n tools.set(name, {\n tool: {\n name,\n description: route?.description ?? `${method.toUpperCase()} ${path}`,\n inputSchema: { type: 'object', properties, ...(required.length > 0 && { required }) },\n annotations: { ...DEFAULT_ANNOTATIONS[method], ...route?.mcp?.annotations },\n },\n method,\n path,\n sources,\n hasBodySchema,\n });\n }\n return tools;\n}\n\n/** Sends a tool's request through the routes on behalf of the MCP request context `c`. */\ntype Dispatch = (request: Request, c: Context) => Response | Promise<Response>;\n\n/** Runs a tool as an HTTP request through the router, so its middleware applies. */\nasync function callTool(\n dispatch: Dispatch,\n target: ToolRoute,\n args: Record<string, unknown>,\n c: Context,\n): Promise<CallToolResult> {\n const fail = (text: string): CallToolResult => ({\n content: [{ type: 'text', text }],\n isError: true,\n });\n const pathArgs: Record<string, unknown> = {};\n const queryArgs: Record<string, unknown> = {};\n const bodyArgs: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(args)) {\n const source = target.sources.get(key) ?? (target.hasBodySchema ? undefined : 'body');\n if (source === 'path') pathArgs[key] = value;\n else if (source === 'query') queryArgs[key] = value;\n else if (source === 'body') bodyArgs[key] = value;\n }\n let missing: string | undefined;\n let invalid: string | undefined;\n const path = target.path.replace(/\\{(\\w+)\\}/g, (_, name: string) => {\n const value = pathArgs[name];\n if (value === undefined || value === null || value === '') missing ??= name;\n // URL parsing resolves dot segments, which would reach a different route.\n if (value === '.' || value === '..') invalid ??= name;\n return encodeURIComponent(String(value ?? ''));\n });\n if (missing) return fail(`Missing path parameter: ${missing}`);\n if (invalid) return fail(`Invalid path parameter: ${invalid}`);\n\n const url = new URL(path, c.request.url);\n for (const [key, value] of Object.entries(queryArgs)) {\n if (typeof value === 'object' && value !== null) return fail(`Invalid query parameter: ${key}`);\n if (value !== undefined && value !== null) url.searchParams.set(key, String(value));\n }\n\n // Forward the caller's headers (Authorization, cookies, ...) but describe the new request.\n const headers = new Headers(c.request.headers);\n for (const name of DROPPED_HEADERS) headers.delete(name);\n headers.set('Accept', 'application/json');\n const hasBody = METHODS_WITH_BODY.includes(target.method);\n if (hasBody) headers.set('Content-Type', 'application/json');\n\n let response: Response;\n try {\n response = await dispatch(\n new Request(url, {\n method: target.method.toUpperCase(),\n headers,\n body: hasBody ? JSON.stringify(bodyArgs) : undefined,\n }),\n c,\n );\n } catch (error) {\n console.error(`Error in MCP tool ${target.tool.name}:`, error);\n return fail('Internal error');\n }\n if (STREAMING_TYPES.test(response.headers.get('Content-Type') ?? '')) {\n await response.body?.cancel();\n return fail('Streaming responses are not supported');\n }\n const text = await response.text();\n const result: CallToolResult = { content: [{ type: 'text', text }], isError: !response.ok };\n if (/^application\\/json/i.test(response.headers.get('Content-Type') ?? '')) {\n const value = parseJson(text);\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n result.structuredContent = value as Record<string, unknown>;\n }\n }\n return result;\n}\n\n/** Handles one MCP request with a fresh stateless server. */\nasync function serve(\n tools: Map<string, ToolRoute>,\n dispatch: Dispatch,\n options: McpOptions,\n c: Context,\n): Promise<Response> {\n const server = new Server(\n { name: options.name, version: options.version },\n { capabilities: { tools: {} }, instructions: options.instructions },\n );\n server.setRequestHandler(ListToolsRequestSchema, () => ({\n tools: [...tools.values()].map((t) => t.tool),\n }));\n server.setRequestHandler(CallToolRequestSchema, (request) => {\n const target = tools.get(request.params.name);\n if (!target) {\n throw new McpError(ErrorCode.InvalidParams, `Unknown tool: ${request.params.name}`);\n }\n return callTool(dispatch, target, request.params.arguments ?? {}, c);\n });\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n return transport.handleRequest(c.request);\n}\n\n/** Adds the RFC 9728 `resource_metadata` parameter to a 401's Bearer challenge. */\nfunction challenge(oauth: McpOAuthOptions): Middleware {\n return async (c, next) => {\n const response = await next();\n if (response.status !== 401) return response;\n const url = new URL(c.request.url);\n const params = [`resource_metadata=\"${url.origin}${url.pathname}${METADATA_PATH}\"`];\n if (oauth.scopes?.length) params.push(`scope=\"${oauth.scopes.join(' ')}\"`);\n const existing = response.headers.get('WWW-Authenticate');\n const value = existing?.startsWith('Bearer ')\n ? `${existing}, ${params.join(', ')}`\n : `Bearer ${params.join(', ')}`;\n const challenged = new Response(response.body, response);\n challenged.headers.set('WWW-Authenticate', value);\n return challenged;\n };\n}\n\n/** Serves protected resource metadata for the resource named by the request path. */\nfunction metadata(oauth: McpOAuthOptions) {\n return (c: Context) => {\n const url = new URL(c.request.url);\n const resource = url.pathname.replace(METADATA_PATH, '');\n return Response.json({\n resource: `${url.origin}${resource}`,\n authorization_servers: oauth.authorizationServers,\n ...(oauth.scopes && { scopes_supported: oauth.scopes }),\n bearer_methods_supported: ['header'],\n });\n };\n}\n\n/**\n * Creates an MCP endpoint exposing routes as tools.\n *\n * Mount the result in a router (e.g. `mcp: mcp({ users }, options, auth)`). Tool names come from\n * `route.operationId` or the method and path (`get_users`, `delete_users_by_id`), descriptions\n * from `route.description`, and flat inputs from the route's path params and query/body schemas.\n *\n * Middleware passed after `options` guards the endpoint itself, and is required unless\n * `options.public` is set; put auth there so unauthenticated\n * clients get an HTTP 401 (with an OAuth challenge when `oauth` is set). Context it adds reaches\n * handlers. Middleware on the given routers runs on every tool call, with the caller's headers\n * forwarded; middleware on their parents elsewhere in the app does not.\n *\n * With `oauth`, metadata is served at `<endpoint>/.well-known/oauth-protected-resource` and\n * referenced from the 401 challenge. Mount {@link oauthProtectedResource} at the origin root as\n * well for clients that probe `/.well-known/oauth-protected-resource/<endpoint>` first.\n *\n * @param routes - A router, or a record of routers mounted under their keys\n * @param options - Server info and optional OAuth settings\n * @param middleware - Middleware for the MCP endpoint\n */\nexport function mcp(\n routes: Router<RouterRoutes> | Record<string, Router<RouterRoutes>>,\n options: McpOptions,\n ...middleware: Middleware[]\n): Router<RouterRoutes> {\n if (middleware.length === 0 && options.public !== true) {\n throw new Error('mcp() needs endpoint middleware such as auth, or public: true');\n }\n const base = isRouter(routes) ? routes : router(routes as RouterRoutes);\n const tools = collectTools(base);\n\n // Context set by middleware on the endpoint and its parents, keyed by dispatched request.\n const inherited = new WeakMap<Request, Record<string, unknown>>();\n const inherit: Middleware = (c, next) => {\n Object.assign(c, inherited.get(c.request));\n return next();\n };\n const fetchRoute = router(base.routes, inherit, ...(base.middleware ?? [])).handler();\n const dispatch: Dispatch = (request, c) => {\n inherited.set(\n request,\n Object.fromEntries(Object.entries(c).filter(([key]) => !CONTEXT_KEYS.has(key))),\n );\n return fetchRoute(request, c.env, c.executionCtx);\n };\n\n const chain = options.oauth ? [challenge(options.oauth), ...middleware] : middleware;\n const notAllowed = () => new Response(null, { status: 405, headers: { Allow: 'POST' } });\n\n return router({\n post: (c) => {\n // The MCP spec requires Origin validation against DNS rebinding.\n const origin = c.request.headers.get('Origin');\n if (origin && origin !== new URL(c.request.url).origin) {\n return new Response('Forbidden origin', { status: 403 });\n }\n return runMiddleware(chain, c as MiddlewareContext, () => serve(tools, dispatch, options, c));\n },\n get: notAllowed,\n delete: notAllowed,\n ...(options.oauth && {\n '.well-known': router({\n 'oauth-protected-resource': router({ get: metadata(options.oauth) }),\n }),\n }),\n });\n}\n\n/**\n * Creates RFC 9728 protected resource metadata routes to mount as `'.well-known'` at the origin\n * root, serving `/.well-known/oauth-protected-resource` and\n * `/.well-known/oauth-protected-resource/<resource>` for single-segment resource paths like `/mcp`.\n *\n * @example\n * ```typescript\n * router({\n * mcp: mcp({ users }, { name: 'api', version: '1.0.0', oauth }, jwtAuth({ ... })),\n * '.well-known': oauthProtectedResource(oauth),\n * });\n * ```\n */\nexport function oauthProtectedResource(oauth: McpOAuthOptions): Router<RouterRoutes> {\n const get = metadata(oauth);\n return router({ 'oauth-protected-resource': router({ get, $resource: router({ get }) }) });\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as SignJwtOptions, c as jwtSign, i as JwtSecret, l as jwtVerify, n as JwtAlgorithm, o as VerifyJwtOptions, r as JwtPayload, s as createJwks, t as Jwks } from "../jwt-B-Fqwu4E.mjs";
|
|
2
|
-
import { i as compose, n as MiddlewareContext, t as Middleware } from "../middleware-
|
|
2
|
+
import { i as compose, n as MiddlewareContext, t as Middleware } from "../middleware-Cj1yHSJF.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/middleware/auth.d.ts
|
|
5
5
|
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* - Optional: `'string?'`, `'number?'`, `'boolean?'`
|
|
11
11
|
* - Arrays: `'string[]'`, `'number[]'`, `'boolean[]'`
|
|
12
12
|
* - Optional arrays: `'string[]?'`, `'number[]?'`, `'boolean[]?'`
|
|
13
|
+
* - Described fields: `['string', 'Description']`, `['number?', 'Optional count']`
|
|
13
14
|
* - Nested objects: `{ field: { nested: 'string' } }`
|
|
14
15
|
*
|
|
15
16
|
* @example
|
|
@@ -18,9 +19,9 @@
|
|
|
18
19
|
* const api = router({
|
|
19
20
|
* users: router({
|
|
20
21
|
* post: route({
|
|
21
|
-
* query: { page: 'number?', limit: 'number?' },
|
|
22
|
+
* query: { page: 'number?', limit: ['number?', 'Max results'] },
|
|
22
23
|
* body: {
|
|
23
|
-
* name: 'string',
|
|
24
|
+
* name: ['string', 'Full name'],
|
|
24
25
|
* age: 'number',
|
|
25
26
|
* tags: 'string[]?',
|
|
26
27
|
* address: { city: 'string', zip: 'string?' },
|
|
@@ -45,9 +46,11 @@ type ArrayType = 'string[]' | 'number[]' | 'boolean[]' | 'unknown[]';
|
|
|
45
46
|
type OptionalArrayType = 'string[]?' | 'number[]?' | 'boolean[]?' | 'unknown[]?';
|
|
46
47
|
/** All supported type strings. */
|
|
47
48
|
type SchemaType = PrimitiveType | OptionalPrimitiveType | ArrayType | OptionalArrayType;
|
|
49
|
+
/** A schema field: a type string, or a `[type, description]` tuple. */
|
|
50
|
+
type SchemaField = SchemaType | readonly [type: SchemaType, description: string];
|
|
48
51
|
/** Schema definition using shorthand syntax. Supports nested objects. */
|
|
49
52
|
interface SchemaDefinition {
|
|
50
|
-
[key: string]:
|
|
53
|
+
[key: string]: SchemaField | SchemaDefinition;
|
|
51
54
|
}
|
|
52
55
|
/** Maps primitive type strings to TypeScript types. */
|
|
53
56
|
type SchemaTypeMap = {
|
|
@@ -69,9 +72,9 @@ type SchemaTypeMap = {
|
|
|
69
72
|
'unknown[]?': unknown[] | undefined;
|
|
70
73
|
};
|
|
71
74
|
/** Checks if a schema field type is optional (ends with ?). */
|
|
72
|
-
type IsOptionalField<T> = T extends `${string}?` ? true : false;
|
|
73
|
-
/** Infers TypeScript type from a schema field (type string or nested object). */
|
|
74
|
-
type InferSchemaField<T> = T extends SchemaType ? SchemaTypeMap[T] : T extends SchemaDefinition ? InferSchema<T> : never;
|
|
75
|
+
type IsOptionalField<T> = T extends `${string}?` ? true : T extends readonly [`${string}?`, string] ? true : false;
|
|
76
|
+
/** Infers TypeScript type from a schema field (type string, tuple, or nested object). */
|
|
77
|
+
type InferSchemaField<T> = T extends SchemaType ? SchemaTypeMap[T] : T extends readonly [infer S extends SchemaType, string] ? SchemaTypeMap[S] : T extends SchemaDefinition ? InferSchema<T> : never;
|
|
75
78
|
/** Gets keys of required fields (non-optional types). */
|
|
76
79
|
type RequiredKeys<T extends SchemaDefinition> = { [K in keyof T]: IsOptionalField<T[K]> extends true ? never : K }[keyof T];
|
|
77
80
|
/** Gets keys of optional fields (types ending with ?). */
|
|
@@ -128,7 +131,7 @@ interface CompiledSchema<T> {
|
|
|
128
131
|
* }
|
|
129
132
|
* ```
|
|
130
133
|
*/
|
|
131
|
-
declare function compileSchema<T extends SchemaDefinition>(schema: T): CompiledSchema<InferSchema<T>>;
|
|
134
|
+
declare function compileSchema<const T extends SchemaDefinition>(schema: T): CompiledSchema<InferSchema<T>>;
|
|
132
135
|
//#endregion
|
|
133
136
|
//#region src/types.d.ts
|
|
134
137
|
/** Standard HTTP methods. */
|
|
@@ -221,8 +224,6 @@ interface RouteDefinition<Q extends SchemaDefinition = {}, B extends SchemaDefin
|
|
|
221
224
|
description?: string;
|
|
222
225
|
/** Operation name, used as the OpenAPI `operationId` and the MCP tool name. */
|
|
223
226
|
operationId?: string;
|
|
224
|
-
/** Field descriptions for docs and MCP, keyed by name (`address.city` for nested fields). */
|
|
225
|
-
descriptions?: Record<string, string>;
|
|
226
227
|
/** MCP tool settings. */
|
|
227
228
|
mcp?: {
|
|
228
229
|
annotations?: ToolAnnotations;
|
|
@@ -399,5 +400,5 @@ declare function runMiddleware(middleware: Middleware[], context: MiddlewareCont
|
|
|
399
400
|
*/
|
|
400
401
|
declare function compose(...middleware: Middleware[]): Middleware;
|
|
401
402
|
//#endregion
|
|
402
|
-
export { InferSchema as C,
|
|
403
|
-
//# sourceMappingURL=middleware-
|
|
403
|
+
export { InferSchema as C, SchemaTypeMap as D, SchemaType as E, ValidationResult as O, CompiledSchema as S, SchemaField as T, TypedHandler as _, runMiddleware as a, isRoute as b, ExecutionContext as c, MethodEntry as d, RouteDefinition as f, ToolAnnotations as g, RouterRoutes as h, compose as i, compileSchema as k, FetchHandler as l, RouterBrand as m, MiddlewareContext as n, CollectPathParams as o, Router as p, MiddlewareNext as r, Context as s, Middleware as t, Method as u, TypedResponse as v, SchemaDefinition as w, isRouter as x, isFunction as y };
|
|
404
|
+
//# sourceMappingURL=middleware-Cj1yHSJF.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware-Cj1yHSJF.d.mts","names":[],"sources":["../src/schema.ts","../src/types.ts","../src/middleware.ts"],"sourcesContent":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"docs-B1IFBS_w.mjs","names":["errors: Record<string, string[]>","resultArray: unknown[]","success","coerced","errorMsg","result: Record<string, unknown>","allErrors: Record<string, string[]>","properties: Record<string, unknown>","required: string[]","paramNames: string[]","propertyToSegment","compiled: CompiledRoute[]","path: Record<string, string>","context: MiddlewareContext","query: unknown","body: unknown","collectedRoutes: CollectedRoute[]","paths: Record<string, Record<string, object>>","operation: Record<string, unknown>","route"],"sources":["../src/schema.ts","../src/types.ts","../src/handler.ts","../src/core.ts","../src/docs.ts"],"sourcesContent":["/**\n * @fileoverview Schema types and validation utilities.\n *\n * Provides shorthand schema definitions for validating query parameters and request bodies.\n * No external dependencies - uses simple string-based type definitions.\n *\n * Supported types:\n * - Primitives: `'string'`, `'number'`, `'boolean'`\n * - Optional: `'string?'`, `'number?'`, `'boolean?'`\n * - Arrays: `'string[]'`, `'number[]'`, `'boolean[]'`\n * - Optional arrays: `'string[]?'`, `'number[]?'`, `'boolean[]?'`\n * - Nested objects: `{ field: { nested: 'string' } }`\n *\n * @example\n * ```typescript\n * // In route definitions\n * const api = router({\n * users: router({\n * post: route({\n * query: { page: 'number?', limit: 'number?' },\n * body: {\n * name: 'string',\n * age: 'number',\n * tags: 'string[]?',\n * address: { city: 'string', zip: 'string?' },\n * },\n * handler: async (c) => {\n * c.query.page; // number | undefined\n * c.body.name; // string\n * c.body.address.city // string\n * },\n * }),\n * }),\n * });\n * ```\n */\n\n/** Primitive type strings. */\nexport type PrimitiveType = 'string' | 'number' | 'boolean' | 'unknown';\n\n/** Optional primitive type strings. */\nexport type OptionalPrimitiveType = 'string?' | 'number?' | 'boolean?' | 'unknown?';\n\n/** Array type strings. */\nexport type ArrayType = 'string[]' | 'number[]' | 'boolean[]' | 'unknown[]';\n\n/** Optional array type strings. */\nexport type OptionalArrayType = 'string[]?' | 'number[]?' | 'boolean[]?' | 'unknown[]?';\n\n/** All supported type strings. */\nexport type SchemaType = PrimitiveType | OptionalPrimitiveType | ArrayType | OptionalArrayType;\n\n/** Schema definition using shorthand syntax. Supports nested objects. */\nexport interface SchemaDefinition {\n [key: string]: SchemaType | SchemaDefinition;\n}\n\n/** Maps primitive type strings to TypeScript types. */\nexport type SchemaTypeMap = {\n string: string;\n 'string?': string | undefined;\n number: number;\n 'number?': number | undefined;\n boolean: boolean;\n 'boolean?': boolean | undefined;\n unknown: unknown;\n 'unknown?': unknown | undefined;\n 'string[]': string[];\n 'string[]?': string[] | undefined;\n 'number[]': number[];\n 'number[]?': number[] | undefined;\n 'boolean[]': boolean[];\n 'boolean[]?': boolean[] | undefined;\n 'unknown[]': unknown[];\n 'unknown[]?': unknown[] | undefined;\n};\n\n/** Checks if a schema field type is optional (ends with ?). */\ntype IsOptionalField<T> = T extends `${string}?` ? true : false;\n\n/** Infers TypeScript type from a schema field (type string or nested object). */\nexport type InferSchemaField<T> = T extends SchemaType\n ? SchemaTypeMap[T]\n : T extends SchemaDefinition\n ? InferSchema<T>\n : never;\n\n/** Gets keys of required fields (non-optional types). */\ntype RequiredKeys<T extends SchemaDefinition> = {\n [K in keyof T]: IsOptionalField<T[K]> extends true ? never : K;\n}[keyof T];\n\n/** Gets keys of optional fields (types ending with ?). */\ntype OptionalKeys<T extends SchemaDefinition> = {\n [K in keyof T]: IsOptionalField<T[K]> extends true ? K : never;\n}[keyof T];\n\n/**\n * Infers TypeScript type from schema definition.\n *\n * Required fields become required properties, optional fields (ending with ?)\n * become optional properties with `| undefined`.\n */\nexport type InferSchema<T extends SchemaDefinition> = {\n [K in RequiredKeys<T>]: InferSchemaField<T[K]>;\n} & {\n [K in OptionalKeys<T>]?: InferSchemaField<T[K]>;\n};\n\n/** Error object returned when validation fails. */\nexport interface ValidationError {\n flatten: () => { fieldErrors: Record<string, string[]> };\n}\n\n/** Validation result matching Zod's safeParse API for compatibility. */\nexport type ValidationResult<T> =\n | { success: true; data: T; error?: undefined }\n | { success: false; data?: undefined; error: ValidationError };\n\n/** Compiled schema with validate method. */\nexport interface CompiledSchema<T> {\n safeParse: (data: object) => ValidationResult<T>;\n}\n\n/** Valid base types for primitives and arrays. */\nconst PRIMITIVE_TYPES = ['string', 'number', 'boolean', 'unknown'];\n\n/** Checks if a schema field is a nested object (not a type string). */\nfunction isNestedSchema(field: SchemaType | SchemaDefinition): field is SchemaDefinition {\n return typeof field === 'object' && field !== null;\n}\n\n/**\n * Validates and coerces a single primitive value.\n * Returns [success, result, errorMessage].\n */\nfunction validatePrimitive(value: unknown, baseType: string): [boolean, unknown, string | null] {\n switch (baseType) {\n case 'string':\n return [true, String(value), null];\n\n case 'number': {\n const num = Number(value);\n if (Number.isNaN(num)) {\n return [false, undefined, 'Expected number'];\n }\n return [true, num, null];\n }\n\n case 'boolean': {\n if (value === 'true' || value === '1' || value === true) {\n return [true, true, null];\n } else if (value === 'false' || value === '0' || value === false) {\n return [true, false, null];\n }\n return [false, undefined, 'Expected boolean (true/false)'];\n }\n\n case 'unknown':\n // Pass through any value without validation.\n return [true, value, null];\n\n default:\n return [false, undefined, `Unknown type: ${baseType}`];\n }\n}\n\n/**\n * Validates a schema field value (primitive, array, or nested object).\n * Returns [success, result, errors].\n */\nfunction validateField(\n key: string,\n field: SchemaType | SchemaDefinition,\n value: unknown,\n parentPath: string = '',\n): [boolean, unknown, Record<string, string[]>] {\n const fieldPath = parentPath ? `${parentPath}.${key}` : key;\n const errors: Record<string, string[]> = {};\n\n // Handle nested object schemas.\n if (isNestedSchema(field)) {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n errors[fieldPath] = ['Expected object'];\n return [false, undefined, errors];\n }\n // Recursively validate nested schema.\n const nestedResult = validateSchema(field, value as Record<string, unknown>, fieldPath);\n return [nestedResult.success, nestedResult.data, nestedResult.errors];\n }\n\n // It's a type string.\n const typeStr = field as string;\n const isOptional = typeStr.endsWith('?');\n const isArray = typeStr.includes('[]');\n\n // Strip optional marker for base type extraction.\n let baseType = isOptional ? typeStr.slice(0, -1) : typeStr;\n\n // Strip array marker.\n if (isArray) {\n baseType = baseType.replace('[]', '');\n }\n\n // Handle missing/empty values.\n if (value === undefined || value === '') {\n if (!isOptional) {\n errors[fieldPath] = ['Required'];\n return [false, undefined, errors];\n }\n return [true, undefined, {}];\n }\n\n // Handle array types.\n if (isArray) {\n if (!Array.isArray(value)) {\n errors[fieldPath] = ['Expected array'];\n return [false, undefined, errors];\n }\n\n const resultArray: unknown[] = [];\n for (let i = 0; i < value.length; i++) {\n const [success, coerced, errorMsg] = validatePrimitive(value[i], baseType);\n if (!success) {\n errors[`${fieldPath}[${i}]`] = [errorMsg!];\n return [false, undefined, errors];\n }\n resultArray.push(coerced);\n }\n return [true, resultArray, {}];\n }\n\n // Handle primitive types.\n const [success, coerced, errorMsg] = validatePrimitive(value, baseType);\n if (!success) {\n errors[fieldPath] = [errorMsg!];\n return [false, undefined, errors];\n }\n return [true, coerced, {}];\n}\n\n/**\n * Validates a full schema against data.\n * Returns { success, data, errors }.\n */\nfunction validateSchema(\n schema: SchemaDefinition,\n data: Record<string, unknown>,\n parentPath: string = '',\n): { success: boolean; data: Record<string, unknown>; errors: Record<string, string[]> } {\n const result: Record<string, unknown> = {};\n const allErrors: Record<string, string[]> = {};\n\n for (const [key, field] of Object.entries(schema)) {\n const value = data[key];\n const [success, coerced, fieldErrors] = validateField(key, field, value, parentPath);\n\n if (!success) {\n Object.assign(allErrors, fieldErrors);\n } else if (coerced !== undefined) {\n result[key] = coerced;\n }\n }\n\n return {\n success: Object.keys(allErrors).length === 0,\n data: result,\n errors: allErrors,\n };\n}\n\n/** Validates schema definition at compile time. */\nfunction validateSchemaDefinition(schema: SchemaDefinition, path: string = ''): void {\n for (const [key, field] of Object.entries(schema)) {\n const fieldPath = path ? `${path}.${key}` : key;\n\n if (isNestedSchema(field)) {\n validateSchemaDefinition(field, fieldPath);\n } else {\n const typeStr = field as string;\n const isOptional = typeStr.endsWith('?');\n let baseType = isOptional ? typeStr.slice(0, -1) : typeStr;\n baseType = baseType.replace('[]', '');\n\n if (!PRIMITIVE_TYPES.includes(baseType)) {\n throw new Error(`Unknown type '${typeStr}' at '${fieldPath}'`);\n }\n }\n }\n}\n\n/**\n * Compiles a schema definition into a validator.\n *\n * Used internally by the router to validate query parameters and request bodies.\n * Provides a Zod-compatible `safeParse` API for consistency.\n *\n * @param schema - The schema definition object\n * @returns A compiled schema with `safeParse` method\n *\n * @example\n * ```typescript\n * const userSchema = compileSchema({\n * name: 'string',\n * age: 'number',\n * email: 'string?',\n * });\n *\n * const result = userSchema.safeParse({ name: 'Alice', age: 30 });\n * if (result.success) {\n * console.log(result.data.name); // 'Alice'\n * } else {\n * console.log(result.error.flatten().fieldErrors);\n * }\n * ```\n */\nexport function compileSchema<T extends SchemaDefinition>(\n schema: T,\n): CompiledSchema<InferSchema<T>> {\n // Validate schema structure at compile time.\n validateSchemaDefinition(schema);\n\n return {\n safeParse(data: object): ValidationResult<InferSchema<T>> {\n const {\n success,\n data: result,\n errors,\n } = validateSchema(schema, data as Record<string, unknown>);\n\n if (!success) {\n return {\n success: false,\n error: { flatten: () => ({ fieldErrors: errors }) },\n };\n }\n\n return { success: true, data: result as InferSchema<T> };\n },\n };\n}\n\n/** JSON Schema object produced by {@link toJsonSchema}. */\nexport interface JsonSchemaObject {\n type: 'object';\n properties: Record<string, unknown>;\n /** Omitted when empty, as OpenAPI 3.0 requires. */\n required?: string[];\n}\n\n/**\n * Converts a schema definition to JSON Schema, recursing into nested objects.\n *\n * @param descriptions - Field descriptions keyed by name, dotted for nested fields\n * @param prefix - Dotted path of `schema` within the root, for nested lookups\n */\nexport function toJsonSchema(\n schema: SchemaDefinition,\n descriptions: Record<string, string> = {},\n prefix = '',\n): JsonSchemaObject {\n const properties: Record<string, unknown> = {};\n const required: string[] = [];\n for (const [key, field] of Object.entries(schema)) {\n const description = descriptions[`${prefix}${key}`];\n const described = description === undefined ? {} : { description };\n if (isNestedSchema(field)) {\n const nested = toJsonSchema(field, descriptions, `${prefix}${key}.`);\n properties[key] = { ...nested, ...described };\n required.push(key);\n continue;\n }\n const optional = field.endsWith('?');\n const base = optional ? field.slice(0, -1) : field;\n const primitive = base.replace('[]', '');\n const item = primitive === 'unknown' ? {} : { type: primitive };\n properties[key] = base.endsWith('[]')\n ? { type: 'array', items: item, ...described }\n : { ...item, ...described };\n if (!optional) required.push(key);\n }\n return { type: 'object', properties, ...(required.length > 0 && { required }) };\n}\n","/**\n * @fileoverview Core type definitions for the routing library.\n *\n * **Path Convention:**\n * - Property names = URL path segments\n * - `$param` prefix = dynamic segment (`:param`)\n * - `get`, `post`, `put`, `patch`, `delete` = HTTP method handlers\n *\n * **Structure:**\n * - `router({ ... })` - groups paths, can have middleware\n * - `route({ query?, body?, handler })` - single method with schemas\n * - Bare functions as shorthand for handlers without schemas\n */\n\nimport type { Middleware } from './middleware.js';\nimport type { InferSchema, SchemaDefinition } from './schema.js';\n\n/** Standard HTTP methods. */\nexport type Method = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'options' | 'head';\n\n/** HTTP method property names. */\nexport const HTTP_METHODS = new Set<string>([\n 'get',\n 'post',\n 'put',\n 'patch',\n 'delete',\n 'options',\n 'head',\n]);\n\n/** Symbol to mark an object as a route definition. */\nexport const ROUTE_MARKER = Symbol.for('@fresho/router:route');\n\n/** Symbol to mark an object as a router. */\nexport const ROUTER_MARKER = Symbol.for('@fresho/router:router');\n\n/**\n * Brand symbol for Router type identification across module boundaries.\n * This enables TypeScript to recognize Router types even when generic\n * type information is lost during cross-package imports.\n */\ndeclare const ROUTER_BRAND: unique symbol;\n\n/** Brand type for Router identification in conditional types. */\nexport type RouterBrand = { readonly [ROUTER_BRAND]: true };\n\n/**\n * Extracts the parameter name from a `$param` property name.\n */\nexport type ExtractParamFromProperty<T extends string> = T extends `$${infer Param}`\n ? Param\n : never;\n\n/**\n * Checks if a property name is a dynamic parameter (starts with $).\n */\nexport type IsParamProperty<T extends string> = T extends `$${string}` ? true : false;\n\n/**\n * Collects path params from a property path array.\n */\nexport type CollectPathParams<Path extends string[]> = Path extends [\n infer Head extends string,\n ...infer Rest extends string[],\n]\n ? (Head extends `$${infer Param}` ? { [K in Param]: string } : {}) & CollectPathParams<Rest>\n : {};\n\n/** Execution context for background tasks (Cloudflare Workers compatible). */\nexport interface ExecutionContext {\n waitUntil(promise: Promise<unknown>): void;\n passThroughOnException(): void;\n}\n\n/**\n * Unified context object passed to handlers.\n */\nexport type Context<\n Q = unknown,\n B = unknown,\n P extends Record<string, string> = Record<string, string>,\n Ctx = {},\n> = {\n /** The original Request. */\n request: Request;\n /** URL path parameters from `$param` segments. */\n path: P;\n /** Query string parameters. */\n query: Q;\n /** Request body. */\n body: B;\n /** Execution context for background tasks. */\n executionCtx?: ExecutionContext;\n /** Environment bindings. */\n env: Ctx extends { env: infer E } ? E : unknown;\n} & Omit<Ctx, 'env'>;\n\n/**\n * Typed response wrapper that carries the response body type.\n */\nexport type TypedResponse<T> = Response & { __responseType?: T };\n\n/**\n * Handler function type.\n */\nexport type TypedHandler<\n Q = unknown,\n B = unknown,\n P extends Record<string, string> = Record<string, string>,\n R = unknown,\n Ctx = {},\n> = (\n context: Context<Q, B, P, Ctx>,\n) => R | Response | TypedResponse<R> | Promise<R | Response | TypedResponse<R>>;\n\n/** MCP tool behavior hints; defaults come from the HTTP method. */\nexport interface ToolAnnotations {\n /** Human-readable tool title. */\n title?: string;\n /** The tool does not modify its environment. */\n readOnlyHint?: boolean;\n /** The tool may perform destructive updates. */\n destructiveHint?: boolean;\n /** Repeating the call with the same arguments has no additional effect. */\n idempotentHint?: boolean;\n /** The tool interacts with external entities. */\n openWorldHint?: boolean;\n}\n\n/**\n * Route definition - a single HTTP method handler with optional schemas.\n *\n * Use `route()` when you need query or body validation schemas.\n * For simple handlers without schemas, use bare functions in the router.\n *\n * @example\n * ```typescript\n * // With schemas\n * get: route({\n * query: { limit: 'number?' },\n * handler: async (c) => ({ items: [], limit: c.query.limit }),\n * })\n *\n * // Without schemas (bare function shorthand)\n * get: async (c) => ({ items: [] })\n * ```\n */\nexport interface RouteDefinition<\n Q extends SchemaDefinition = {},\n B extends SchemaDefinition = {},\n R = unknown,\n P extends Record<string, string> = Record<string, string>,\n Ctx = {},\n> {\n /** Query parameter schema for validation. */\n query?: Q;\n /** Request body schema for validation. */\n body?: B;\n /** Optional description for documentation. */\n description?: string;\n /** Operation name, used as the OpenAPI `operationId` and the MCP tool name. */\n operationId?: string;\n /** Field descriptions for docs and MCP, keyed by name (`address.city` for nested fields). */\n descriptions?: Record<string, string>;\n /** MCP tool settings. */\n mcp?: { annotations?: ToolAnnotations };\n /** The request handler function. */\n handler: TypedHandler<InferSchema<Q>, InferSchema<B>, P, R, Ctx>;\n}\n\n/** A bare handler function (shorthand for route without schemas). */\nexport type BareHandler = (\n context: Context<unknown, unknown, Record<string, string>, unknown>,\n) => unknown;\n\n/** Checks if a value is a function. */\nexport function isFunction(value: unknown): value is BareHandler {\n return typeof value === 'function';\n}\n\n/** Checks if entry is a Route (marked with ROUTE_MARKER). */\nexport function isRoute(entry: unknown): entry is RouteDefinition {\n return typeof entry === 'object' && entry !== null && ROUTE_MARKER in entry;\n}\n\n/** Checks if entry is a Router (marked with ROUTER_MARKER). */\nexport function isRouter(entry: unknown): entry is Router<RouterRoutes> {\n return typeof entry === 'object' && entry !== null && ROUTER_MARKER in entry;\n}\n\n/**\n * A method handler - either a route with schemas or a bare function.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type MethodEntry = RouteDefinition<any, any, any, any, any> | BareHandler;\n\n/**\n * Router routes record.\n *\n * Can contain:\n * - `get`, `post`, `put`, `patch`, `delete` - method handlers (route or function)\n * - Other property names - nested routers (path segments)\n * - `$param` properties - dynamic path segments\n */\nexport type RouterRoutes = {\n get?: MethodEntry;\n post?: MethodEntry;\n put?: MethodEntry;\n patch?: MethodEntry;\n delete?: MethodEntry;\n options?: MethodEntry;\n head?: MethodEntry;\n} & {\n [key: string]: MethodEntry | Router<RouterRoutes>;\n};\n\n/** Handler function signature for the standalone router. */\nexport type FetchHandler = (\n request: Request,\n env?: unknown,\n ctx?: ExecutionContext,\n) => Response | Promise<Response>;\n\n/**\n * Router - groups paths and can have middleware.\n *\n * @example\n * ```typescript\n * const api = router({\n * // Method handlers for this path\n * get: async (c) => [...],\n * post: route({ body: { name: 'string' }, handler: async (c) => c.body }),\n *\n * // Nested path segments\n * users: router({\n * get: async (c) => [...],\n * $id: router({\n * get: async (c) => ({ id: c.path.id }),\n * }),\n * }),\n * });\n * ```\n */\nexport interface Router<T extends RouterRoutes> extends RouterBrand {\n /** Route definitions and nested routers. */\n readonly routes: T;\n /** Middleware applied to all routes in this router. */\n readonly middleware?: Middleware[];\n /**\n * Creates a fetch handler for use with Cloudflare Workers, Deno, Bun, etc.\n */\n handler(): FetchHandler;\n}\n","/**\n * @fileoverview HTTP request handler for @fresho/router.\n *\n * Builds paths from property names:\n * - Regular properties = path segments\n * - `$param` properties = dynamic segments (`:param`)\n * - `get`, `post`, etc. = method handlers\n */\n\nimport type { Middleware, MiddlewareContext } from './middleware.js';\nimport { runMiddleware } from './middleware.js';\nimport type { CompiledSchema } from './schema.js';\nimport { compileSchema } from './schema.js';\nimport type {\n ExecutionContext,\n FetchHandler,\n RouteDefinition,\n Router,\n RouterRoutes,\n} from './types.js';\nimport { HTTP_METHODS, isFunction, isRoute, isRouter } from './types.js';\n\n/** Compiled route with pattern matcher and handler. */\ninterface CompiledRoute {\n method: string;\n pattern: RegExp;\n paramNames: string[];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n handler: (context: any) => unknown;\n middleware: Middleware[];\n querySchema?: CompiledSchema<unknown>;\n bodySchema?: CompiledSchema<unknown>;\n}\n\n/**\n * Escapes special regex characters in a string.\n */\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Converts a path with :param segments to a regex pattern.\n */\nfunction pathToRegex(path: string): { pattern: RegExp; paramNames: string[] } {\n const paramNames: string[] = [];\n const parts = path.split(/:(\\w+)/g);\n let regexStr = '';\n\n for (let i = 0; i < parts.length; i++) {\n if (i % 2 === 0) {\n regexStr += escapeRegex(parts[i]);\n } else {\n paramNames.push(parts[i]);\n regexStr += '([^/]+)';\n }\n }\n\n return {\n pattern: new RegExp(`^${regexStr}$`),\n paramNames,\n };\n}\n\n/**\n * Converts a property name to a path segment.\n * - `$param` becomes `:param`\n * - Other names become literal segments\n */\nfunction propertyToSegment(prop: string): string {\n if (prop.startsWith('$')) {\n return `:${prop.slice(1)}`;\n }\n return prop;\n}\n\n/** Collects and compiles all routes from a router tree. */\nfunction compileRoutes(\n routerDef: Router<RouterRoutes>,\n parentPath = '',\n parentMiddleware: Middleware[] = [],\n): CompiledRoute[] {\n const currentMiddleware = [...parentMiddleware, ...(routerDef.middleware || [])];\n const compiled: CompiledRoute[] = [];\n\n for (const [prop, entry] of Object.entries(routerDef.routes)) {\n // Check if entry is a nested router first (takes precedence over method name check).\n // This allows path segments named 'get', 'post', etc. when they contain routers.\n if (isRouter(entry)) {\n // Nested router - recurse with updated path.\n const segment = propertyToSegment(prop);\n const newPath = parentPath ? `${parentPath}/${segment}` : `/${segment}`;\n compiled.push(...compileRoutes(entry, newPath, currentMiddleware));\n } else if (HTTP_METHODS.has(prop)) {\n // Method handler (get, post, etc.) - only when entry is a route or function.\n const method = prop.toUpperCase();\n const path = parentPath || '/';\n const { pattern, paramNames } = pathToRegex(path);\n\n if (isFunction(entry)) {\n // Bare function handler.\n compiled.push({\n method,\n pattern,\n paramNames,\n handler: entry,\n middleware: currentMiddleware,\n });\n } else if (isRoute(entry)) {\n // Route with schemas.\n const routeDef = entry as RouteDefinition;\n compiled.push({\n method,\n pattern,\n paramNames,\n handler: routeDef.handler,\n middleware: currentMiddleware,\n querySchema: routeDef.query ? compileSchema(routeDef.query) : undefined,\n bodySchema: routeDef.body ? compileSchema(routeDef.body) : undefined,\n });\n }\n }\n }\n\n return compiled;\n}\n\n/**\n * Strips the body from a response for HEAD requests.\n */\nfunction stripBodyForHead(response: Response, isHead: boolean): Response {\n if (!isHead) return response;\n return new Response(null, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n}\n\n/**\n * Creates a fetch handler from a router definition.\n *\n * @example\n * ```typescript\n * const api = router({\n * health: router({\n * get: async () => ({ status: 'ok' }),\n * }),\n * users: router({\n * get: async () => [...],\n * $id: router({\n * get: async (c) => ({ id: c.path.id }),\n * }),\n * }),\n * });\n *\n * export default { fetch: api.handler() };\n * ```\n */\nexport function createHandler<T extends RouterRoutes>(routerDef: Router<T>): FetchHandler {\n const compiledRoutes = compileRoutes(routerDef);\n\n return async (request: Request, env?: unknown, ctx?: ExecutionContext): Promise<Response> => {\n const url = new URL(request.url);\n const method = request.method.toUpperCase();\n const isHead = method === 'HEAD';\n\n // Find matching route.\n for (const compiled of compiledRoutes) {\n const methodMatches =\n compiled.method === method ||\n method === 'OPTIONS' ||\n (method === 'HEAD' && compiled.method === 'GET');\n if (!methodMatches) continue;\n\n const match = compiled.pattern.exec(url.pathname);\n if (!match) continue;\n\n // Extract path parameters.\n const path: Record<string, string> = {};\n for (let i = 0; i < compiled.paramNames.length; i++) {\n path[compiled.paramNames[i]] = decodeURIComponent(match[i + 1]);\n }\n\n // Create middleware context.\n const context: MiddlewareContext = {\n request,\n path,\n query: {},\n body: {},\n env,\n executionCtx: ctx,\n };\n\n // Run middleware and handler.\n if (\n compiled.middleware.length > 0 ||\n compiled.handler ||\n compiled.querySchema ||\n compiled.bodySchema\n ) {\n const response = await runMiddleware(compiled.middleware, context, async () => {\n const routeId = `${compiled.method} ${url.pathname}`;\n\n // Validate query params.\n let query: unknown = {};\n if (compiled.querySchema) {\n const raw = Object.fromEntries(url.searchParams);\n const result = compiled.querySchema.safeParse(raw);\n if (!result.success) {\n return Response.json(\n {\n error: 'Invalid query parameters',\n route: routeId,\n details: result.error.flatten(),\n },\n { status: 400 },\n );\n }\n query = result.data;\n }\n\n // Validate body.\n let body: unknown = {};\n if (compiled.bodySchema && ['POST', 'PUT', 'PATCH'].includes(method)) {\n const raw = await request.json().catch(() => ({}));\n const result = compiled.bodySchema.safeParse(raw);\n if (!result.success) {\n return Response.json(\n { error: 'Invalid request body', route: routeId, details: result.error.flatten() },\n { status: 400 },\n );\n }\n body = result.data;\n }\n\n context.query = query;\n context.body = body;\n\n if (compiled.handler) {\n // Build handler context with middleware extensions.\n const handlerContext = {\n request,\n path,\n query,\n body,\n env,\n executionCtx: ctx,\n };\n\n // Copy middleware extensions.\n for (const key of Object.keys(context)) {\n if (!['request', 'path', 'query', 'body', 'env', 'executionCtx'].includes(key)) {\n (handlerContext as Record<string, unknown>)[key] = (\n context as Record<string, unknown>\n )[key];\n }\n }\n\n const result = await compiled.handler(handlerContext);\n if (result instanceof Response) {\n return result;\n }\n return Response.json(result);\n }\n\n return Response.json({});\n });\n return stripBodyForHead(response, isHead);\n }\n\n return stripBodyForHead(Response.json({}), isHead);\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n };\n}\n","/**\n * @fileoverview Core route and router creation functions.\n *\n * ## Path Convention\n * - Property names = URL path segments\n * - `$param` prefix = dynamic segment (`:param`)\n * - `get`, `post`, `put`, `patch`, `delete` = HTTP method handlers\n *\n * ## Typing Best Practices\n *\n * **Schemas** (`query`/`body`) provide runtime validation AND type inference.\n * Use them for any input that needs validation:\n * ```typescript\n * get: route({\n * query: { limit: 'number?' },\n * handler: async (c) => c.query.limit, // limit: number | undefined\n * })\n * ```\n *\n * **Context** (`route.ctx<T>()`) provides types only (no validation).\n * Use it for path params, env bindings, and middleware-injected values:\n * ```typescript\n * interface MyContext {\n * path: { id: string };\n * env: { DB: Database };\n * user: { name: string }; // from auth middleware\n * }\n * get: route.ctx<MyContext>()({\n * handler: async (c) => ({ id: c.path.id, user: c.user.name }),\n * })\n * ```\n *\n * **Important:** Don't add explicit type annotations to handler parameters.\n * Let types flow from schemas and context:\n * ```typescript\n * // GOOD: types inferred from schema\n * handler: async (c) => c.query.limit\n *\n * // BAD: redundant type annotation\n * handler: async (c: { query: { limit?: number } }) => c.query.limit\n * ```\n */\n\nimport { createHandler } from './handler.js';\nimport type { Middleware } from './middleware.js';\nimport type { SchemaDefinition } from './schema.js';\nimport type { RouteDefinition, Router, RouterRoutes } from './types.js';\nimport { ROUTE_MARKER, ROUTER_MARKER } from './types.js';\n\n/**\n * Creates a route with query/body validation schemas.\n *\n * Use `route()` when you need request validation. For simple handlers\n * without validation, you can use bare functions directly in the router.\n *\n * @example\n * ```typescript\n * // Route with query validation\n * get: route({\n * query: { limit: 'number?', offset: 'number?' },\n * handler: async (c) => {\n * const items = await db.list(c.query.limit, c.query.offset);\n * return { items };\n * },\n * })\n *\n * // Route with body validation\n * post: route({\n * body: { name: 'string', email: 'string' },\n * handler: async (c) => {\n * const user = await db.create(c.body);\n * return user;\n * },\n * })\n *\n * // Simple handler without validation (bare function)\n * get: async () => ({ status: 'ok' })\n * ```\n */\nexport function route<\n const Q extends SchemaDefinition = {},\n const B extends SchemaDefinition = {},\n R = unknown,\n>(definition: RouteDefinition<Q, B, R, {}, {}>): RouteDefinition<Q, B, R, {}, {}> {\n return { ...definition, [ROUTE_MARKER]: true } as RouteDefinition<Q, B, R, {}, {}>;\n}\n\n/** Return type of route.ctx<T>() - callable and chainable. */\ninterface CtxBuilder<Ctx> {\n <const Q extends SchemaDefinition = {}, const B extends SchemaDefinition = {}, R = unknown>(\n definition: RouteDefinition<Q, B, R, {}, Ctx>,\n ): RouteDefinition<Q, B, R, {}, Ctx>;\n\n ctx<AdditionalCtx>(): CtxBuilder<Ctx & AdditionalCtx>;\n}\n\n/** Creates a chainable context builder. */\nfunction createCtxBuilder<Ctx>(): CtxBuilder<Ctx> {\n const builder = <\n const Q extends SchemaDefinition = {},\n const B extends SchemaDefinition = {},\n R = unknown,\n >(\n definition: RouteDefinition<Q, B, R, {}, Ctx>,\n ): RouteDefinition<Q, B, R, {}, Ctx> =>\n ({ ...definition, [ROUTE_MARKER]: true }) as RouteDefinition<Q, B, R, {}, Ctx>;\n\n builder.ctx = <AdditionalCtx>() => createCtxBuilder<Ctx & AdditionalCtx>();\n\n return builder as CtxBuilder<Ctx>;\n}\n\n/**\n * Creates a route with typed context for values not covered by schemas.\n *\n * Use `route.ctx<T>()` to type:\n * - **Path params**: `{ path: { id: string } }` for `$id` segments\n * - **Env bindings**: `{ env: { DB: Database } }` for runtime environment\n * - **Middleware values**: `{ user: User }` for auth middleware, etc.\n *\n * Context provides types only, not runtime validation. For validated inputs,\n * use `query` and `body` schemas instead.\n *\n * @example\n * ```typescript\n * interface AppContext {\n * path: { id: string };\n * env: { DB: D1Database };\n * user: { id: string }; // injected by auth middleware\n * }\n *\n * $id: router({\n * get: route.ctx<AppContext>()({\n * query: { include: 'string?' }, // schema for validation\n * handler: async (c) => {\n * // c.path.id, c.env.DB, c.user.id - from context\n * // c.query.include - from schema\n * return c.env.DB.get(c.path.id);\n * },\n * }),\n * })\n * ```\n */\nroute.ctx = <Ctx>(): CtxBuilder<Ctx> => createCtxBuilder<Ctx>();\n\n/**\n * Creates a router that groups paths and applies middleware.\n *\n * @example\n * ```typescript\n * const api = router({\n * health: router({\n * get: async () => ({ status: 'ok' }),\n * }),\n *\n * users: router({\n * // GET /users - list users\n * get: route({\n * query: { limit: 'number?' },\n * handler: async (c) => db.users.list(c.query.limit),\n * }),\n *\n * // POST /users - create user\n * post: route({\n * body: { name: 'string', email: 'string' },\n * handler: async (c) => db.users.create(c.body),\n * }),\n *\n * // /users/:id\n * $id: router({\n * get: async (c) => db.users.get(c.path.id),\n * delete: async (c) => db.users.delete(c.path.id),\n * }),\n * }),\n * });\n *\n * export default { fetch: api.handler() };\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function router<T extends RouterRoutes, M extends Middleware<any>[]>(\n routes: T,\n ...middleware: M\n): Router<T> {\n // Cast needed because RouterBrand uses a type-only symbol declaration.\n const self = {\n routes,\n middleware: middleware.length > 0 ? middleware : undefined,\n handler() {\n return createHandler(self);\n },\n } as unknown as Router<T>;\n // Add marker for runtime type checking.\n (self as unknown as Record<symbol, boolean>)[ROUTER_MARKER] = true;\n return self;\n}\n","/**\n * @fileoverview OpenAPI documentation generation.\n *\n * Generates OpenAPI 3.0 documentation from router definitions.\n *\n * **Path Convention:**\n * - Property names = URL path segments\n * - `$param` prefix = dynamic segment (`{param}` in OpenAPI)\n * - `get`, `post`, `put`, `patch`, `delete` = HTTP method handlers\n */\n\nimport { type SchemaDefinition, toJsonSchema } from './schema.js';\nimport type { RouteDefinition, Router, RouterRoutes } from './types.js';\nimport { HTTP_METHODS, isFunction, isRoute, isRouter } from './types.js';\n\n/** Collected route info for documentation. */\nexport interface CollectedRoute {\n path: string;\n method: string;\n route: RouteDefinition | null;\n}\n\n/** Converts property name to path segment, handling $param convention. */\nfunction propertyToSegment(prop: string): string {\n if (prop.startsWith('$')) {\n return `{${prop.slice(1)}}`;\n }\n return prop;\n}\n\n/** Collects all routes from a router tree, with `{param}` path segments. */\nexport function collectRoutes(routerDef: Router<RouterRoutes>, parentPath = ''): CollectedRoute[] {\n const collectedRoutes: CollectedRoute[] = [];\n\n for (const [prop, entry] of Object.entries(routerDef.routes)) {\n // Check if this is a method handler.\n if (HTTP_METHODS.has(prop)) {\n const path = parentPath || '/';\n if (isFunction(entry)) {\n collectedRoutes.push({ path, method: prop, route: null });\n } else if (isRoute(entry)) {\n collectedRoutes.push({ path, method: prop, route: entry });\n }\n } else if (isRouter(entry)) {\n // Nested router - recurse with updated path.\n const segment = propertyToSegment(prop);\n const newPath = parentPath ? `${parentPath}/${segment}` : `/${segment}`;\n collectedRoutes.push(...collectRoutes(entry, newPath));\n }\n }\n\n return collectedRoutes;\n}\n\n/** Generates OpenAPI documentation from a router. */\nexport function generateDocs(config: {\n title: string;\n version: string;\n description?: string;\n router: Router<RouterRoutes>;\n}): object {\n const paths: Record<string, Record<string, object>> = {};\n const allRoutes = collectRoutes(config.router);\n\n for (const { path, method, route } of allRoutes) {\n if (!paths[path]) {\n paths[path] = {};\n }\n\n const operation: Record<string, unknown> = {\n operationId: route?.operationId,\n description: route?.description,\n responses: {\n 200: { description: 'Successful response' },\n 400: { description: 'Validation error' },\n },\n };\n\n // Add query parameters (only primitive types supported in query strings).\n if (route?.query) {\n const querySchema = route.query as SchemaDefinition;\n operation.parameters = Object.entries(querySchema)\n .filter(([, type]) => typeof type === 'string')\n .map(([name, type]) => ({\n name,\n in: 'query',\n required: !(type as string).endsWith('?'),\n description: route.descriptions?.[name],\n schema: { type: (type as string).replace('?', '').replace('[]', '') },\n }));\n }\n\n // Add request body.\n if (route?.body && ['post', 'put', 'patch'].includes(method)) {\n operation.requestBody = {\n required: true,\n content: {\n 'application/json': {\n schema: toJsonSchema(route.body as SchemaDefinition, route.descriptions),\n },\n },\n };\n }\n\n paths[path][method] = operation;\n }\n\n return {\n openapi: '3.0.0',\n info: {\n title: config.title,\n version: config.version,\n description: config.description,\n },\n paths,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"middleware-zRMBpTLe.d.mts","names":[],"sources":["../src/schema.ts","../src/types.ts","../src/middleware.ts"],"sourcesContent":[],"mappings":"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"}
|