@minisylar/express-typed-router 1.4.4 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,13 @@
1
1
  # @minisylar/express-typed-router
2
2
 
3
- A strongly-typed Express router with Zod validation and automatic type inference for params, body, query, and middleware.
3
+ A strongly-typed Express router with schema validation and automatic type inference for params, body, query, and middleware.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - 🚀 **Full TypeScript support** with automatic type inference for route parameters
8
- - 🛡️ **Zod validation** for request body, query parameters, and route params
8
+ - 🛡️ **Schema validation** for request body, query parameters, and route params (Zod, Yup, Valibot, Arktype,Joi,Effect,decoders,
9
+ ts.data.json,
10
+ unhoax, etc.)
9
11
  - 🔗 **Express.js compatibility** - works with Express 4 and Express 5
10
12
  - 🤝 **Mix with existing Express routes** - seamlessly integrates with your current codebase
11
13
  - 📝 **JSDoc documentation** with comprehensive examples
@@ -22,32 +24,53 @@ pnpm add @minisylar/express-typed-router
22
24
  yarn add @minisylar/express-typed-router
23
25
  ```
24
26
 
25
- > **Note:** This package requires Express 4.18.0+ or Express 5.0.0+ and Zod 3.25.0+
27
+ > **Note:** This package requires Express 4.18.0+ or Express 5.0.0+. For schema validation the library works with multiple popular schema libraries (examples below).
26
28
 
27
- ### Zod Compatibility
29
+ ### Schema Compatibility
28
30
 
29
- This library supports **both Zod 3 and Zod 4** simultaneously! The library follows [Zod's official recommendations for library authors](https://zod.dev/library-authors) and uses versioned imports internally to ensure compatibility with both versions.
31
+ This library is schema-agnostic: it provides a validation plumbing that works with multiple popular schema libraries. Below are short examples showing how you can use different schema libraries with the router. The router expects a schema-like object that can validate input; most adapters are straightforward.
30
32
 
31
- - **Zod v3.25.0+** - Full support
32
- - ✅ **Zod v4** - Full support including Core and Mini packages
33
- - 🔄 **Runtime detection** - Automatically detects and handles both versions
34
-
35
- You can use schemas from either version or mix them in the same application:
33
+ Example with Zod (v3 or v4):
36
34
 
37
35
  ```typescript
38
- // Works with Zod 4
39
- import { z } from "zod/v4";
36
+ import { z } from "zod"; // or "zod/v4" or "zod/v3" as needed
40
37
  const userSchema = z.object({ name: z.string() });
38
+ router.post("/users", { bodySchema: userSchema }, handler);
39
+ ```
41
40
 
42
- // Also works with Zod 3
43
- import * as z4 from "zod/v3";
44
- const postSchema = z4.object({ title: z4.string() });
41
+ Example with Yup:
45
42
 
46
- // Both schemas work seamlessly with the router
43
+ ```javascript
44
+ import * as yup from "yup";
45
+ const userSchema = yup.object({ name: yup.string().required() });
46
+ // pass the yup schema directly as bodySchema; the router will run validation
47
+ router.post("/users", { bodySchema: userSchema }, handler);
48
+ ```
49
+
50
+ Example with Valibot (valibot):
51
+
52
+ ```typescript
53
+ import { object, string } from "valibot";
54
+ const userSchema = object({ name: string() });
47
55
  router.post("/users", { bodySchema: userSchema }, handler);
48
- router.post("/posts", { bodySchema: postSchema }, handler);
49
56
  ```
50
57
 
58
+ Example with Arktype:
59
+
60
+ ```typescript
61
+ import { object, string } from "arktype";
62
+ const userSchema = object({ name: string() });
63
+ router.post("/users", { bodySchema: userSchema }, handler);
64
+ ```
65
+
66
+ If a schema library needs an adapter (for example to map its errors to the router's error format), add a small wrapper that runs validation and throws the expected error shape. See the project's examples for concrete adapter patterns.
67
+
68
+ Note about Joi: Joi's TypeScript typings do not reliably infer the output type from the runtime schema shape. When using Joi you should either:
69
+
70
+ - provide an explicit generic type for the schema (e.g. `Joi.object<MyType>(...)`),
71
+ - add a variable type annotation (e.g. `const s: Joi.ObjectSchema<MyType> = Joi.object(...)`), or
72
+ - write a small adapter that validates at runtime and exposes a typed result to TypeScript.
73
+
51
74
  ## Quick Start
52
75
 
53
76
  ```javascript
@@ -0,0 +1 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=s(require(`express`)),l=s(require(`@standard-schema/utils`));function u(e,t){let n=e;if(n&&n[`~standard`]&&typeof n[`~standard`].validate==`function`){let e=n[`~standard`].validate(t);if(e instanceof Promise)throw TypeError(`Async schema validation is not supported by parseSchema`);if(e.issues)throw new l.SchemaError(e.issues);return e.value}throw TypeError(`Unsupported schema shape for parseSchema`)}function d(e,t){let n=e;if(n&&n[`~standard`]&&typeof n[`~standard`].validate==`function`)return n[`~standard`].validate(t);if(n&&typeof n.safeParse==`function`)return n.safeParse(t);if(n&&typeof n.parse==`function`)try{let e=n.parse(t);return{value:e}}catch(e){return{issues:[{message:e?.message??String(e)}]}}if(n&&typeof n.validate==`function`){let e=n.validate(t);return e&&e.then&&typeof e.then==`function`?e.then(e=>e.error?{issues:[{message:e.error.message}]}:e.issues?{issues:e.issues}:{value:e.value??e}):e&&e.error?{issues:[{message:e.error.message}]}:e&&e.issues?{issues:e.issues}:{value:e.value??e}}return{issues:[{message:`Unsupported schema shape`}]}}function f(e){return typeof e==`object`&&!!e&&`issues`in e&&Array.isArray(e.issues)}var p=class{router;constructor(){this.router=c.default.Router()}useMiddleware(e){return this.router.use(e),this}getRouter(){return this.router}get(e,t,n){return this.registerRoute(`get`,e,t,n)}post(e,t,n){return this.registerRoute(`post`,e,t,n)}put(e,t,n){return this.registerRoute(`put`,e,t,n)}patch(e,t,n){return this.registerRoute(`patch`,e,t,n)}delete(e,t,n){return this.registerRoute(`delete`,e,t,n)}options(e,t,n){return this.registerRoute(`options`,e,t,n)}head(e,t,n){return this.registerRoute(`head`,e,t,n)}all(e,t,n){return this.registerRoute(`all`,e,t,n)}registerRoute(e,t,n,r){let i=[];if(typeof n==`object`){let e=n;e.middleware&&i.push(...e.middleware),e.bodySchema&&i.push(this.createBodyValidationMiddleware(e.bodySchema)),e.querySchema&&i.push(this.createQueryValidationMiddleware(e.querySchema)),i.push(r)}else i.push(n);return this.router[e](t,...i),this}createBodyValidationMiddleware(e){return async(t,n,r)=>{try{let i=d(e,t.body),a=i&&typeof i.then==`function`?await i:i;if(a&&`issues`in a&&a.issues){n.status(400).json({error:`Validation failed`,details:a.errors||a.issues});return}t.body=a&&`value`in a?a.value:a,r()}catch(e){f(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):r(e)}}}createQueryValidationMiddleware(e){return async(t,n,r)=>{try{let i=d(e,t.query),a=i&&typeof i.then==`function`?await i:i;if(a&&`issues`in a&&a.issues){n.status(400).json({error:`Validation failed`,details:a.errors||a.issues});return}let o=a&&`value`in a?a.value:a;Object.defineProperty(t,`query`,{value:o,writable:!1,enumerable:!0,configurable:!0}),r()}catch(e){f(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):r(e)}}}};function m(){return new p}function h(e){let t=new p;return e?.errorHandler&&t.getRouter().use(e.errorHandler),t}function g(...e){let t=new p;for(let n of e)t=t.useMiddleware(n);return t}exports.createTypedRouter=m,exports.createTypedRouterWithConfig=h,exports.createTypedRouterWithMiddleware=g,exports.isSchemaError=f,exports.parseSchema=u,exports.safeParseSchema=d;
@@ -0,0 +1,406 @@
1
+ import express, { NextFunction, Request, Response } from "express";
2
+ import { StandardSchemaV1 } from "@standard-schema/spec";
3
+
4
+ //#region src/schema-router.d.ts
5
+
6
+ type AnyStandardSchema = StandardSchemaV1<any, any>;
7
+ type InferOutput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<T> : unknown;
8
+ type InferInput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferInput<T> : unknown;
9
+ type InferFromSafeParse<T> = T extends {
10
+ safeParse: (...args: any[]) => infer R;
11
+ } ? R extends {
12
+ success: true;
13
+ data: infer O;
14
+ } ? O : R extends Promise<infer PR> ? PR extends {
15
+ success: true;
16
+ data: infer O;
17
+ } ? O : never : never : T extends {
18
+ parse: (...args: any[]) => infer R;
19
+ } ? R : never;
20
+ type InferSchemaOutput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<T> : InferFromSafeParse<T>;
21
+ declare function parseSchema<T>(schema: T, data: unknown): InferSchemaOutput<T>;
22
+ type SafeParseResult<T> = StandardSchemaV1.Result<InferSchemaOutput<T>> | {
23
+ value: InferSchemaOutput<T>;
24
+ } | {
25
+ issues: any[];
26
+ } | Promise<StandardSchemaV1.Result<InferSchemaOutput<T>> | {
27
+ value: InferSchemaOutput<T>;
28
+ } | {
29
+ issues: any[];
30
+ }>;
31
+ declare function safeParseSchema<T>(schema: T, data: unknown): SafeParseResult<T>;
32
+ declare function isSchemaError(error: unknown): error is {
33
+ issues: any[];
34
+ };
35
+ /**
36
+ * Extract route parameters from Express.js route patterns.
37
+ *
38
+ * Supports all Express.js routing patterns:
39
+ * - Named parameters: /users/:userId → { userId: string }
40
+ * - Multiple parameters: /users/:userId/books/:bookId → { userId: string; bookId: string }
41
+ * - Parameters with separators: /flights/:from-:to → { from: string; to: string }
42
+ * - Dot notation: /plantae/:genus.:species → { genus: string; species: string }
43
+ * - Regex constraints: /user/:id(\d+) → { id: string }
44
+ * - Optional parameters: /posts/:year/:month? → { year: string; month?: string }
45
+ * - Wildcard parameters: /files/* → { "0": string }
46
+ * - Multiple wildcards: /a/star/b/star → { "0": string; "1": string }
47
+ */
48
+ type ExtractRouteParams<Path extends string> = string extends Path ? Record<string, string> : ExtractParams<Path>;
49
+ /**
50
+ * Main parameter extraction logic - enhanced for Express 5 support with recursion depth limit
51
+ */
52
+ type ExtractParams<Path extends string> = Path extends `${infer Before}{${infer OptionalContent}}${infer After}` ? ExtractOptionalSegment<OptionalContent> & ExtractParams<`${Before}${After}`> : Path extends `${infer _Before}:${infer Rest}` ? ExtractSingleParam<Rest> & ExtractParams<RemoveFirstParam<Path>> : Path extends `${infer _Before}*${infer After}` ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<After> : {};
53
+ /**
54
+ * Extract parameters from Express 5 optional segments in braces
55
+ * Handles patterns like {/:param}, {.:ext}, {/optional/:param}
56
+ */
57
+ type ExtractOptionalSegment<Content extends string> = Content extends `/:${infer Rest}` ? ExtractOptionalParam<Rest> : Content extends `.:${infer Rest}` ? ExtractOptionalParam<Rest> : Content extends `${infer _Path}:${infer Rest}` ? ExtractOptionalParam<Rest> : {};
58
+ /**
59
+ * Extract a single optional parameter from brace content
60
+ */
61
+ type ExtractOptionalParam<Rest extends string> = Rest extends `${infer ParamName}/${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}-${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}.${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}` ? { [K in ParamName]?: string } : {};
62
+ /**
63
+ * Extract a single parameter name from the rest of the path
64
+ * Enhanced to handle Express 5 patterns and optional parameters correctly
65
+ * Special handling for consecutive parameters like :from-:to
66
+ * Order matters: regex constraints must be handled before repeating parameters
67
+ */
68
+ type ExtractSingleParam<Rest extends string> = Rest extends `${infer ParamName}(${infer _Constraint})${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}-:${infer _NextParam}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}.:${infer _NextParam}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}?/${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}?-${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}?.${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}?#${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}?:${infer _After}` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}/${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}-${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}.${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}#${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}:${infer _After}` ? { [K in ParamName]: string } : Rest extends `${infer ParamName}+${infer _After}` ? { [K in ParamName]: string[] } : Rest extends `${infer ParamName}*${infer _After}` ? { [K in ParamName]?: string[] } : Rest extends `${infer ParamName}?${infer _After}` ? { [K in ParamName]?: string } : Rest extends string ? Rest extends "" ? {} : Rest extends `${infer ParamName}?` ? { [K in ParamName]?: string } : Rest extends `${infer ParamName}+` ? { [K in ParamName]: string[] } : Rest extends `${infer ParamName}*` ? { [K in ParamName]?: string[] } : { [K in Rest]: string } : {};
69
+ /**
70
+ * Remove the first parameter from path to continue parsing
71
+ * Enhanced to handle Express 5 patterns and optional parameters
72
+ * Handles patterns like :from-:to by removing just :from and keeping -:to
73
+ * Order matters: regex constraints must be handled before repeating parameters
74
+ */
75
+ type RemoveFirstParam<Path extends string> = Path extends `${infer Before}:${infer Rest}` ? Rest extends `${infer _ParamName}(${infer _Constraint})${infer After}` ? `${Before}${After}` : Rest extends `${infer _ParamName}-:${infer After}` ? `${Before}-:${After}` : Rest extends `${infer _ParamName}.:${infer After}` ? `${Before}.:${After}` : Rest extends `${infer _ParamName}?/${infer After}` ? `${Before}/${After}` : Rest extends `${infer _ParamName}?-${infer After}` ? `${Before}${After}` : Rest extends `${infer _ParamName}?.${infer After}` ? `${Before}${After}` : Rest extends `${infer _ParamName}?#${infer After}` ? `${Before}${After}` : Rest extends `${infer _ParamName}?:${infer After}` ? `${Before}:${After}` : Rest extends `${infer _ParamName}/${infer After}` ? `${Before}/${After}` : Rest extends `${infer _ParamName}-${infer After}` ? `${Before}${After}` : Rest extends `${infer _ParamName}.${infer After}` ? `${Before}${After}` : Rest extends `${infer _ParamName}#${infer After}` ? `${Before}${After}` : Rest extends `${infer _ParamName}:${infer After}` ? `${Before}:${After}` : Rest extends `${infer _ParamName}+${infer After}` ? `${Before}${After}` : Rest extends `${infer _ParamName}*${infer After}` ? `${Before}${After}` : Rest extends `${infer _ParamName}?${infer After}` ? `${Before}${After}` : Before : Path;
76
+ /**
77
+ * Count wildcards to assign proper numeric indices with recursion depth limit
78
+ */
79
+ type CountWildcards<Path extends string, Count extends string = "0"> = Path extends `${infer _Before}*${infer Rest}` ? CountWildcards<Rest, IncrementWildcard<Count>> : Count;
80
+ /**
81
+ * Helper type to increment wildcard count as string
82
+ */
83
+ type IncrementWildcard<T extends string> = T extends "0" ? "1" : T extends "1" ? "2" : T extends "2" ? "3" : T extends "3" ? "4" : T extends "4" ? "5" : T extends "5" ? "6" : T extends "6" ? "7" : T extends "7" ? "8" : T extends "8" ? "9" : "10";
84
+ /**
85
+ * Express middleware that adds custom properties to the request object and/or response locals.
86
+ *
87
+ * @template TReq - The shape of the properties added to the request object.
88
+ * @template TLocals - The shape of the properties added to response.locals.
89
+ * @param req - The Express request object, extended with TReq.
90
+ * @param res - The Express response object with typed locals.
91
+ * @param next - The next middleware function.
92
+ */
93
+ type TypedMiddleware<TReq extends Record<string, any> = {}, TLocals extends Record<string, any> = {}> = (req: Request & TReq, res: Response<any, TLocals>, next: NextFunction) => void | Promise<void>;
94
+ /**
95
+ * Simplified TypedMiddleware for request-only extensions (backward compatibility)
96
+ */
97
+ type RequestOnlyMiddleware<TReq extends Record<string, any>> = TypedMiddleware<TReq, {}>;
98
+ /**
99
+ * Simplified TypedMiddleware for response locals-only extensions
100
+ */
101
+ type LocalsOnlyMiddleware<TLocals extends Record<string, any>> = TypedMiddleware<{}, TLocals>;
102
+ type InferMiddlewareProps<T extends readonly TypedMiddleware<any, any>[]> = T extends readonly [infer First, ...infer Rest] ? First extends TypedMiddleware<infer FirstReq, any> ? Rest extends readonly TypedMiddleware<any, any>[] ? FirstReq & InferMiddlewareProps<Rest> : FirstReq : {} : {};
103
+ type InferMiddlewareLocals<T extends readonly TypedMiddleware<any, any>[]> = T extends readonly [infer First, ...infer Rest] ? First extends TypedMiddleware<any, infer FirstLocals> ? Rest extends readonly TypedMiddleware<any, any>[] ? FirstLocals & InferMiddlewareLocals<Rest> : FirstLocals : {} : {};
104
+ type SchemaRequest<Path extends string = string, BodySchema extends AnyStandardSchema | unknown = unknown, QuerySchema extends AnyStandardSchema | unknown = unknown, MiddlewareProps extends Record<string, any> = {}> = Omit<Request, "params" | "query" | "body"> & {
105
+ params: ExtractRouteParams<Path>;
106
+ body: BodySchema extends unknown ? InferSchemaOutput<BodySchema> : unknown;
107
+ query: QuerySchema extends unknown ? InferSchemaOutput<QuerySchema> : unknown;
108
+ } & MiddlewareProps;
109
+ type SchemaRouteHandler<Path extends string = string, BodySchema extends AnyStandardSchema | unknown = unknown, QuerySchema extends AnyStandardSchema | unknown = unknown, MiddlewareProps extends Record<string, any> = {}, ResponseLocals extends Record<string, any> = {}> = (req: SchemaRequest<Path, BodySchema, QuerySchema, MiddlewareProps>, res: Response<any, ResponseLocals>, next?: NextFunction) => void | undefined | Promise<void | undefined> | Response | Promise<Response> | Promise<Response | undefined>;
110
+ /**
111
+ * Options for defining a typed route, including schemas and middleware.
112
+ *
113
+ * @template BodySchema - Schema for request body validation.
114
+ * @template QuerySchema - Schema for query parameter validation.
115
+ * @property bodySchema - Optional schema for validating the request body.
116
+ * @property querySchema - Optional schema for validating the query string.
117
+ * @property middleware - Optional array of TypedMiddleware for this route.
118
+ */
119
+ interface RouteOptions<BodySchema extends AnyStandardSchema | unknown = unknown, QuerySchema extends AnyStandardSchema | unknown = unknown> {
120
+ bodySchema?: BodySchema;
121
+ querySchema?: QuerySchema;
122
+ middleware?: TypedMiddleware<any, any>[];
123
+ }
124
+ type HttpMethod = "get" | "post" | "put" | "delete" | "patch" | "options" | "head" | "all";
125
+ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}, RouterLocals extends Record<string, any> = {}> {
126
+ private router;
127
+ constructor();
128
+ /**
129
+ * Add typed middleware that extends the request with additional properties
130
+ * and/or adds properties to response.locals
131
+ */ /**
132
+ * Add typed middleware to the router.
133
+ * This middleware will apply to all routes defined after this call.
134
+ *
135
+ * @template TReq - Type extensions for the request object
136
+ * @template TLocals - Type extensions for response.locals
137
+ * @param middleware - The typed middleware function
138
+ * @returns A new router instance with updated types
139
+ */
140
+ useMiddleware<TReq extends Record<string, any> = {}, TLocals extends Record<string, any> = {}>(middleware: TypedMiddleware<TReq, TLocals>): TypedRouter<RouterMiddlewareProps & TReq, RouterLocals & TLocals>;
141
+ /**
142
+ * Get the underlying Express router
143
+ */
144
+ getRouter(): express.Router;
145
+ get<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
146
+ get<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
147
+ get<Path extends string, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
148
+ middleware: Middleware;
149
+ }, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<Middleware>, RouterLocals & InferMiddlewareLocals<Middleware>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
150
+ get<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: RouteOptions<BodySchema, QuerySchema> & {
151
+ middleware: [...M];
152
+ },
153
+ // Using tuple spread pattern
154
+ handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
155
+ // Make it readonly for type inference
156
+ // Make it readonly for type inference
157
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
158
+ post<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
159
+ bodySchema: BodySchema;
160
+ querySchema?: QuerySchema;
161
+ middleware: [...M];
162
+ }, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
163
+ // Make it readonly for type inference
164
+ // Make it readonly for type inference
165
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
166
+ post<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
167
+ bodySchema: BodySchema;
168
+ middleware: [...M];
169
+ },
170
+ // Using tuple spread pattern
171
+ handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
172
+ // Make it readonly for type inference
173
+ // Make it readonly for type inference
174
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
175
+ post<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
176
+ middleware: [...M];
177
+ },
178
+ // Using tuple spread pattern
179
+ handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
180
+ // Make it readonly for type inference
181
+ // Make it readonly for type inference
182
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
183
+ post<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
184
+ post<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
185
+ put<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
186
+ bodySchema: BodySchema;
187
+ querySchema?: QuerySchema;
188
+ middleware: [...M];
189
+ }, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
190
+ // Make it readonly for type inference
191
+ // Make it readonly for type inference
192
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
193
+ put<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
194
+ bodySchema: BodySchema;
195
+ middleware: [...M];
196
+ },
197
+ // Using tuple spread pattern
198
+ handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
199
+ // Make it readonly for type inference
200
+ // Make it readonly for type inference
201
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
202
+ put<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
203
+ middleware: [...M];
204
+ },
205
+ // Using tuple spread pattern
206
+ handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
207
+ // Make it readonly for type inference
208
+ // Make it readonly for type inference
209
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
210
+ put<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
211
+ put<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
212
+ patch<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
213
+ bodySchema: BodySchema;
214
+ querySchema?: QuerySchema;
215
+ middleware: [...M];
216
+ }, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
217
+ // Make it readonly for type inference
218
+ // Make it readonly for type inference
219
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
220
+ patch<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
221
+ bodySchema: BodySchema;
222
+ middleware: [...M];
223
+ },
224
+ // Using tuple spread pattern
225
+ handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
226
+ // Make it readonly for type inference
227
+ // Make it readonly for type inference
228
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
229
+ patch<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
230
+ middleware: [...M];
231
+ },
232
+ // Using tuple spread pattern
233
+ handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
234
+ // Make it readonly for type inference
235
+ // Make it readonly for type inference
236
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
237
+ patch<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
238
+ patch<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
239
+ delete<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
240
+ querySchema: QuerySchema;
241
+ middleware: [...M];
242
+ },
243
+ // Using tuple spread pattern
244
+ handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
245
+ // Make it readonly for type inference
246
+ // Make it readonly for type inference
247
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
248
+ delete<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
249
+ querySchema: QuerySchema;
250
+ }, handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
251
+ delete<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
252
+ middleware: [...M];
253
+ },
254
+ // Using tuple spread pattern
255
+ handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
256
+ // Make it readonly for type inference
257
+ // Make it readonly for type inference
258
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
259
+ delete<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
260
+ options<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
261
+ querySchema: QuerySchema;
262
+ middleware: [...M];
263
+ },
264
+ // Using tuple spread pattern
265
+ handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
266
+ // Make it readonly for type inference
267
+ // Make it readonly for type inference
268
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
269
+ options<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
270
+ querySchema: QuerySchema;
271
+ }, handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
272
+ options<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
273
+ middleware: [...M];
274
+ },
275
+ // Using tuple spread pattern
276
+ handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
277
+ // Make it readonly for type inference
278
+ // Make it readonly for type inference
279
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
280
+ options<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
281
+ head<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
282
+ querySchema: QuerySchema;
283
+ middleware: [...M];
284
+ },
285
+ // Using tuple spread pattern
286
+ handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
287
+ // Make it readonly for type inference
288
+ // Make it readonly for type inference
289
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
290
+ head<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
291
+ querySchema: QuerySchema;
292
+ }, handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
293
+ head<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
294
+ middleware: [...M];
295
+ },
296
+ // Using tuple spread pattern
297
+ handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
298
+ // Make it readonly for type inference
299
+ // Make it readonly for type inference
300
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
301
+ head<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
302
+ all<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
303
+ bodySchema: BodySchema;
304
+ querySchema?: QuerySchema;
305
+ middleware: [...M];
306
+ }, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
307
+ // Make it readonly for type inference
308
+ // Make it readonly for type inference
309
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
310
+ all<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
311
+ bodySchema: BodySchema;
312
+ middleware: [...M];
313
+ },
314
+ // Using tuple spread pattern
315
+ handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
316
+ // Make it readonly for type inference
317
+ // Make it readonly for type inference
318
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
319
+ all<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
320
+ querySchema: QuerySchema;
321
+ middleware: [...M];
322
+ },
323
+ // Using tuple spread pattern
324
+ handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
325
+ // Make it readonly for type inference
326
+ // Make it readonly for type inference
327
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
328
+ all<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
329
+ all<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
330
+ middleware: [...M];
331
+ },
332
+ // Using tuple spread pattern
333
+ handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
334
+ // Make it readonly for type inference
335
+ // Make it readonly for type inference
336
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
337
+ all<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
338
+ private registerRoute;
339
+ private createBodyValidationMiddleware;
340
+ private createQueryValidationMiddleware;
341
+ }
342
+ /**
343
+ * Create a new strongly-typed Express router instance.
344
+ *
345
+ * This is the simplest way to get started with @minisylar/express-typed-router.
346
+ *
347
+ * @example
348
+ * import { createTypedRouter } from '@minisylar/express-typed-router';
349
+ *
350
+ * // Create a router and add a typed GET route
351
+ * const router = createTypedRouter();
352
+ * router.get('/hello/:name', (req, res) => {
353
+ * // req.params.name is typed as string
354
+ * res.json({ message: `Hello, ${req.params.name}!` });
355
+ * });
356
+ *
357
+ * // Use with Express
358
+ * import express from 'express';
359
+ * const app = express();
360
+ * app.use('/api', router.getRouter());
361
+ */
362
+ declare function createTypedRouter<RouterMiddlewareProps extends Record<string, any> = {}, RouterLocals extends Record<string, any> = {}>(): TypedRouter<RouterMiddlewareProps, RouterLocals>;
363
+ /**
364
+ * Configuration options for createTypedRouterWithConfig.
365
+ *
366
+ * @property validateInput - (Future) Whether to enable global input validation.
367
+ * @property errorHandler - Optional global error handler middleware for the router.
368
+ */
369
+ interface RouterConfig {
370
+ validateInput?: boolean;
371
+ errorHandler?: (error: any, req: Request, res: Response, next: NextFunction) => void;
372
+ }
373
+ /**
374
+ * Create a new typed router with optional configuration.
375
+ *
376
+ * Use this if you want to add a global error handler or future global options.
377
+ *
378
+ * @param config - Optional configuration for the router (e.g. error handler).
379
+ * @returns A new TypedRouter instance.
380
+ *
381
+ * @example
382
+ * import { createTypedRouterWithConfig } from '@minisylar/express-typed-router';
383
+ *
384
+ * const router = createTypedRouterWithConfig({
385
+ * errorHandler: (err, req, res, next) => {
386
+ * res.status(500).json({ error: 'Something went wrong', details: err });
387
+ * }
388
+ * });
389
+ */
390
+ declare function createTypedRouterWithConfig<RouterMiddlewareProps extends Record<string, any> = {}, RouterLocals extends Record<string, any> = {}>(config?: RouterConfig): TypedRouter<RouterMiddlewareProps, RouterLocals>;
391
+ /**
392
+ * Create a new typed router with pre-configured middleware.
393
+ *
394
+ * This is useful for setting up router-level middleware in a single call.
395
+ *
396
+ * @param middleware - One or more TypedMiddleware functions to apply to all routes.
397
+ * @returns A new TypedRouter instance with the middleware applied.
398
+ *
399
+ * @example
400
+ * import { createTypedRouterWithMiddleware } from '@minisylar/express-typed-router';
401
+ *
402
+ * const router = createTypedRouterWithMiddleware(authMiddleware, loggingMiddleware);
403
+ */
404
+ declare function createTypedRouterWithMiddleware<T extends Record<string, any>>(...middleware: TypedMiddleware<any, any>[]): TypedRouter<T>;
405
+ //#endregion
406
+ export { AnyStandardSchema, ExtractRouteParams, HttpMethod, InferInput, InferOutput, InferSchemaOutput, LocalsOnlyMiddleware, RequestOnlyMiddleware, RouteOptions, RouterConfig, SafeParseResult, SchemaRequest, SchemaRouteHandler, TypedMiddleware, createTypedRouter, createTypedRouterWithConfig, createTypedRouterWithMiddleware, isSchemaError, parseSchema, safeParseSchema };