@minisylar/express-typed-router 1.1.0 → 1.2.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
@@ -118,16 +118,28 @@ app.listen(3000);
118
118
 
119
119
  ### Global Middleware
120
120
 
121
+ **Important**: Unlike Express, middleware must be applied using method chaining or capturing returned routers. See the [FAQ section](#faq-and-common-patterns) for details.
122
+
121
123
  ```typescript
124
+ // Method chaining pattern (recommended)
122
125
  const router = createTypedRouter()
123
- .useTypedMiddleware(authMiddleware)
124
- .useTypedMiddleware(loggingMiddleware)
125
- .useTypedMiddleware(timestampMiddleware);
126
+ .useMiddleware(authMiddleware)
127
+ .useMiddleware(loggingMiddleware)
128
+ .useMiddleware(timestampMiddleware);
126
129
 
127
130
  // All routes automatically get types from all middleware
128
131
  router.get("/protected", (req, res) => {
129
132
  // req.userId, req.requestId, req.timestamp all available and typed
130
133
  });
134
+
135
+ // Alternative: capturing returned router
136
+ const baseRouter = createTypedRouter();
137
+ const routerWithMiddleware = baseRouter
138
+ .useMiddleware(authMiddleware)
139
+ .useMiddleware(loggingMiddleware);
140
+
141
+ // Use the router with middleware applied
142
+ routerWithMiddleware.get("/users", handler);
131
143
  ```
132
144
 
133
145
  ### Per-Route Middleware
@@ -136,7 +148,7 @@ router.get("/protected", (req, res) => {
136
148
  router.get(
137
149
  "/admin/:userId",
138
150
  {
139
- middleware: [adminMiddleware, auditMiddleware] as const,
151
+ middleware: [adminMiddleware, auditMiddleware],
140
152
  },
141
153
  (req, res) => {
142
154
  // Types from both global AND per-route middleware are merged
@@ -191,13 +203,13 @@ app.use(express.json());
191
203
 
192
204
  // Create router and define middleware inline (automatically typed!)
193
205
  const router = createTypedRouter()
194
- .useTypedMiddleware((req, res, next) => {
206
+ .useMiddleware((req, res, next) => {
195
207
  const token = req.headers.authorization;
196
208
  req.userId = "user123";
197
209
  req.isAdmin = token?.includes("admin") || false;
198
210
  next();
199
211
  })
200
- .useTypedMiddleware((req, res, next) => {
212
+ .useMiddleware((req, res, next) => {
201
213
  req.requestId = Math.random().toString(36);
202
214
  console.log(`[${req.requestId}] ${req.method} ${req.path}`);
203
215
  next();
@@ -223,7 +235,7 @@ router.get(
223
235
  },
224
236
  (req, res) => {
225
237
  // All properties are automatically typed:
226
- const { userId } = req.params; // string (auto-inferred from route)
238
+ const { userId } = req.params; // string (auto-inferred from route)
227
239
  const { include, limit } = req.query; // from schema validation
228
240
  const { userId: authUserId, isAdmin, requestId } = req; // from middleware
229
241
 
@@ -266,19 +278,21 @@ router.post(
266
278
  router.delete(
267
279
  "/users/:userId",
268
280
  {
269
- middleware: [(req, res, next) => {
270
- if (!req.isAdmin) {
271
- return res.status(403).json({ error: "Admin required" });
272
- }
273
- req.hasAdminAccess = true;
274
- next();
275
- }] as const,
281
+ middleware: [
282
+ (req, res, next) => {
283
+ if (!req.isAdmin) {
284
+ return res.status(403).json({ error: "Admin required" });
285
+ }
286
+ req.hasAdminAccess = true;
287
+ next();
288
+ },
289
+ ], // No need for 'as const' - middleware arrays are automatically typed
276
290
  },
277
291
  (req, res) => {
278
292
  // Types from BOTH global middleware AND per-route middleware
279
- const { userId } = req.params; // From route
293
+ const { userId } = req.params; // From route
280
294
  const { userId: authUserId, requestId } = req; // From global middleware
281
- const { hasAdminAccess } = req; // From per-route middleware
295
+ const { hasAdminAccess } = req; // From per-route middleware
282
296
 
283
297
  res.json({
284
298
  deleted: userId,
@@ -326,8 +340,8 @@ const loggingMiddleware: TypedMiddleware<{ requestId: string }> = (
326
340
 
327
341
  // Use the explicitly typed middleware
328
342
  const router = createTypedRouter()
329
- .useTypedMiddleware(authMiddleware)
330
- .useTypedMiddleware(loggingMiddleware);
343
+ .useMiddleware(authMiddleware)
344
+ .useMiddleware(loggingMiddleware);
331
345
 
332
346
  // Rest of the routes work the same way...
333
347
  ```
@@ -354,7 +368,7 @@ const authMiddleware: TypedMiddleware<{ userId: string; isAdmin: boolean }> = (
354
368
  };
355
369
 
356
370
  // Add to router - types are automatically merged
357
- router.useTypedMiddleware(authMiddleware);
371
+ router.useMiddleware(authMiddleware);
358
372
 
359
373
  router.get("/protected", (req, res) => {
360
374
  // TypeScript knows about req.userId and req.isAdmin
@@ -381,7 +395,7 @@ const adminMiddleware: TypedMiddleware<{ hasAdminAccess: true }> = (
381
395
  router.get(
382
396
  "/admin/:userId",
383
397
  {
384
- middleware: [adminMiddleware] as const,
398
+ middleware: [adminMiddleware], // No need for 'as const' - arrays are automatically typed
385
399
  },
386
400
  (req, res) => {
387
401
  // Types from BOTH global and per-route middleware are available
@@ -589,8 +603,8 @@ Creates a typed router instance with full flexibility for middleware and validat
589
603
  const router = createTypedRouter();
590
604
 
591
605
  // Add global middleware (chainable)
592
- router.useTypedMiddleware(middleware1)
593
- .useTypedMiddleware(middleware2);
606
+ router.useMiddleware(middleware1)
607
+ .useMiddleware(middleware2);
594
608
 
595
609
  // All HTTP methods supported
596
610
  router.get(path, options?, handler)
@@ -697,6 +711,177 @@ pnpm build:watch
697
711
 
698
712
  ISC
699
713
 
714
+ ## FAQ and Common Patterns
715
+
716
+ ### Middleware Behavior Differences from Express
717
+
718
+ #### IMPORTANT: Router Middleware and Route Registration
719
+
720
+ When using middleware with `express-typed-router`, there's an important difference from standard Express behavior:
721
+
722
+ **In Express**, middleware added with `router.use()` applies to all routes registered _after_ it:
723
+
724
+ ```javascript
725
+ // Express middleware behavior
726
+ const router = express.Router();
727
+ router.use(authMiddleware); // Apply middleware
728
+ router.get("/route1", handler1); // Has authMiddleware
729
+ router.use(logMiddleware); // Apply another middleware
730
+ router.get("/route2", handler2); // Has BOTH auth and log middleware
731
+ ```
732
+
733
+ **In express-typed-router**, `useMiddleware()` returns a _new router instance_ for type safety:
734
+
735
+ ```typescript
736
+ // ❌ WON'T WORK - middleware not applied to route
737
+ const router = createTypedRouter();
738
+ router.useMiddleware(authMiddleware); // Returns new router that isn't captured
739
+ router.get("/route", handler); // Original router without middleware!
740
+
741
+ // ✅ CORRECT - chain methods (recommended)
742
+ const router = createTypedRouter()
743
+ .useMiddleware(authMiddleware)
744
+ .get("/route", handler);
745
+
746
+ // ✅ CORRECT - chain directly from middleware call
747
+ const router = createTypedRouter();
748
+ router.useMiddleware(authMiddleware).get("/route", handler);
749
+
750
+ // ✅ ALSO CORRECT - use per-route middleware
751
+ const router = createTypedRouter();
752
+ router.get("/route", { middleware: [authMiddleware] }, handler);
753
+ ```
754
+
755
+ This design is necessary for full type safety but requires a different pattern than standard Express.
756
+
757
+ ### Common Express Patterns vs express-typed-router
758
+
759
+ Here are common Express patterns and how to achieve them with express-typed-router:
760
+
761
+ #### Pattern 1: Adding middleware to specific routes
762
+
763
+ **Express:**
764
+
765
+ ```javascript
766
+ const router = express.Router();
767
+ router.get("/public", publicHandler);
768
+ router.use(authMiddleware); // Only affects routes below
769
+ router.get("/private", privateHandler); // Has authMiddleware
770
+ ```
771
+
772
+ **express-typed-router:**
773
+
774
+ ```typescript
775
+ // Option 1: Separate routers
776
+ const publicRouter = createTypedRouter();
777
+ publicRouter.get("/public", publicHandler);
778
+
779
+ const privateRouter = createTypedRouter().useMiddleware(authMiddleware);
780
+ privateRouter.get("/private", privateHandler);
781
+
782
+ // Combine in Express
783
+ app.use(publicRouter.getRouter());
784
+ app.use(privateRouter.getRouter());
785
+
786
+ // Option 2: Per-route middleware
787
+ const router = createTypedRouter();
788
+ router.get("/public", publicHandler);
789
+ router.get("/private", { middleware: [authMiddleware] }, privateHandler);
790
+ ```
791
+
792
+ #### Pattern 2: Adding middleware for a group of routes
793
+
794
+ **Express:**
795
+
796
+ ```javascript
797
+ const router = express.Router();
798
+ router.get("/public", handler);
799
+
800
+ // Only admin routes have auth middleware
801
+ const adminRouter = express.Router();
802
+ adminRouter.use(authMiddleware);
803
+ adminRouter.get("/users", adminHandler1);
804
+ adminRouter.get("/settings", adminHandler2);
805
+
806
+ router.use("/admin", adminRouter);
807
+ ```
808
+
809
+ **express-typed-router:**
810
+
811
+ ```typescript
812
+ const publicRouter = createTypedRouter();
813
+ publicRouter.get("/public", handler);
814
+
815
+ // Admin router with middleware
816
+ const adminRouter = createTypedRouter().useMiddleware(authMiddleware);
817
+ adminRouter.get("/users", adminHandler1);
818
+ adminRouter.get("/settings", adminHandler2);
819
+
820
+ // Combine with Express
821
+ app.use(publicRouter.getRouter());
822
+ app.use("/admin", adminRouter.getRouter());
823
+ ```
824
+
825
+ #### Pattern 3: Middleware with dynamically added routes
826
+
827
+ **Express:**
828
+
829
+ ```javascript
830
+ const router = express.Router();
831
+ router.use(middleware);
832
+
833
+ // Later, routes are added dynamically
834
+ function addRoute(path, handler) {
835
+ router.get(path, handler); // Has middleware
836
+ }
837
+ ```
838
+
839
+ **express-typed-router:**
840
+
841
+ ```typescript
842
+ // Option 1: Pass the router to the function
843
+ const router = createTypedRouter().useMiddleware(middleware);
844
+
845
+ function addRoute(router, path, handler) {
846
+ router.get(path, handler);
847
+ }
848
+
849
+ // Option 2: Factory function
850
+ function createRouteAdder(middleware) {
851
+ const router = createTypedRouter().useMiddleware(middleware);
852
+
853
+ return {
854
+ addRoute: (path, handler) => router.get(path, handler),
855
+ getRouter: () => router.getRouter(),
856
+ };
857
+ }
858
+
859
+ const routeAdder = createRouteAdder(middleware);
860
+ routeAdder.addRoute("/path", handler);
861
+ app.use(routeAdder.getRouter());
862
+ ```
863
+
864
+ ### Using express-typed-router in JavaScript
865
+
866
+ JavaScript users don't need to worry about TypeScript types but should still follow the middleware chaining pattern:
867
+
868
+ ```javascript
869
+ // JavaScript usage
870
+ const { createTypedRouter } = require("@minisylar/express-typed-router");
871
+
872
+ const router = createTypedRouter().useMiddleware((req, res, next) => {
873
+ req.user = { id: "user123" };
874
+ next();
875
+ });
876
+
877
+ router.get("/users", (req, res) => {
878
+ // req.user is available but not typed (JavaScript doesn't have types)
879
+ res.json({ userId: req.user.id });
880
+ });
881
+
882
+ module.exports = router.getRouter();
883
+ ```
884
+
700
885
  ## Contributing
701
886
 
702
887
  Contributions are welcome! Please feel free to submit a Pull Request.
@@ -34,7 +34,16 @@ var TypedRouter = class {
34
34
  * Add typed middleware that extends the request with additional properties
35
35
  * and/or adds properties to response.locals
36
36
  */
37
- useTypedMiddleware(middleware) {
37
+ /**
38
+ * Add typed middleware to the router.
39
+ * This middleware will apply to all routes defined after this call.
40
+ *
41
+ * @template TReq - Type extensions for the request object
42
+ * @template TLocals - Type extensions for response.locals
43
+ * @param middleware - The typed middleware function
44
+ * @returns A new router instance with updated types
45
+ */
46
+ useMiddleware(middleware) {
38
47
  this.router.use(middleware);
39
48
  return this;
40
49
  }
@@ -169,7 +178,7 @@ function createTypedRouterWithConfig(config) {
169
178
  */
170
179
  function createTypedRouterWithMiddleware(...middleware) {
171
180
  let router = new TypedRouter();
172
- for (const mw of middleware) router = router.useTypedMiddleware(mw);
181
+ for (const mw of middleware) router = router.useMiddleware(mw);
173
182
  return router;
174
183
  }
175
184
 
@@ -1 +1 @@
1
- {"version":3,"file":"zod-router.cjs","names":["middleware: TypedMiddleware<TReq, TLocals>","path: string","optionsOrHandler: any","handler?: any","method: HttpMethod","middlewares: any[]","schema: ZodType<any, any, any>","req: Request","res: Response","next: NextFunction","z","config?: RouterConfig"],"sources":["../src/zod-router.ts"],"sourcesContent":["/**\n * @packageDocumentation\n * @module @minisylar/express-typed-router\n *\n * @title @minisylar/express-typed-router\n *\n * A strongly-typed Express router with Zod validation and automatic type inference for params, body, query, and middleware.\n *\n * @example\n * // Example 1: Basic usage with router-level middleware\n * const router = createTypedRouter()\n * .useTypedMiddleware(timestampMiddleware)\n * .useTypedMiddleware(requestIdMiddleware)\n *\n * router.get('/posts/:postId', (req, res) => {\n * const { postId } = req.params // Typed as { postId: string }\n * const { timestamp, requestId } = req // Both properties are now typed correctly!\n * res.json({ postId, timestamp, requestId })\n * })\n *\n * @example\n * // Example 2: Per-route middleware with automatic type inference\n * router.post(\n * '/posts',\n * {\n * bodySchema: CreatePostSchema,\n * middleware: [timestampMiddleware, requestIdMiddleware] as const\n * },\n * (req, res) => {\n * const { title, content, tags } = req.body // From schema validation\n * const { timestamp, requestId } = req // From middleware - should be automatically typed!\n * res.json({ title, content, tags, timestamp, requestId })\n * }\n * )\n *\n * @example\n * // Example 3: Mixed middleware\n * const router = createTypedRouter().useTypedMiddleware(requestIdMiddleware)\n * router.get(\n * '/posts/:postId',\n * {\n * middleware: [authMiddleware] as const\n * },\n * (req, res) => {\n * const { postId } = req.params\n * const { requestId } = req // From router-level middleware\n * const { userId, hasPermission } = req // From per-route middleware\n * res.json({ postId, requestId, userId, hasPermission })\n * }\n * )\n *\n * @example\n * // Example 4: Using factory with pre-configured middleware\n * const router = createTypedRouterWithMiddleware(timestampMiddleware, requestIdMiddleware)\n * router.get('/simple/:id', (req, res) => {\n * const { id } = req.params\n * const { timestamp, requestId } = req // Already available from factory setup!\n * res.json({ id, timestamp, requestId })\n * })\n *\n * @example\n * // Example 5: Demonstrating all HTTP methods\n * const router = createTypedRouter().useTypedMiddleware(requestIdMiddleware)\n *\n * // GET with query validation\n * router.get('/posts', { querySchema: QuerySchema }, (req, res) => {\n * const { limit, offset } = req.query // Typed from schema\n * const { requestId } = req // From router middleware\n * res.json({ posts: [], limit, offset, requestId })\n * })\n *\n * // POST with body validation and middleware\n * router.post(\n * '/posts',\n * {\n * bodySchema: CreatePostSchema,\n * middleware: [timestampMiddleware] as const\n * },\n * (req, res) => {\n * const { title, content } = req.body // From body schema\n * const { requestId, timestamp } = req // From middleware\n * res.json({ id: 'new-post', title, content, requestId, timestamp })\n * }\n * )\n *\n * // PUT for full updates\n * router.put(\n * '/posts/:postId',\n * {\n * bodySchema: CreatePostSchema,\n * middleware: [authMiddleware] as const\n * },\n * (req, res) => {\n * const { postId } = req.params\n * const { title, content } = req.body\n * const { requestId, userId, hasPermission } = req\n * res.json({ postId, title, content, requestId, userId, hasPermission })\n * }\n * )\n *\n * // PATCH for partial updates\n * router.patch('/posts/:postId', { bodySchema: UpdatePostSchema }, (req, res) => {\n * const { postId } = req.params\n * const updates = req.body // Partial update object\n * const { requestId } = req\n * res.json({ postId, updates, requestId })\n * })\n *\n * // DELETE\n * router.delete('/posts/:postId', (req, res) => {\n * const { postId } = req.params\n * const { requestId } = req\n * res.json({ deleted: postId, requestId })\n * })\n *\n * // OPTIONS for CORS preflight\n * router.options('/posts/*', (req, res) => {\n * res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE')\n * res.header('Access-Control-Allow-Headers', 'Content-Type')\n * res.status(200).end()\n * })\n *\n * // HEAD for metadata only\n * router.head('/posts/:postId', (req, res) => {\n * const { postId } = req.params\n * res.header('X-Post-ID', postId)\n * res.status(200).end()\n * })\n *\n * // ALL method for catch-all routes\n * router.all('/debug/*', (req, res) => {\n * const { requestId } = req\n * res.json({\n * method: req.method,\n * path: req.path,\n * requestId\n * })\n * })\n */\n/* eslint-disable @typescript-eslint/no-empty-object-type */\nimport express, {\n type Request,\n type Response,\n type NextFunction,\n} from \"express\";\nimport { z, type ZodType } from \"zod\";\n\n/**\n * Extract route parameters from Express.js route patterns.\n *\n * Supports all Express.js routing patterns:\n * - Named parameters: /users/:userId → { userId: string }\n * - Multiple parameters: /users/:userId/books/:bookId → { userId: string; bookId: string }\n * - Parameters with separators: /flights/:from-:to → { from: string; to: string }\n * - Dot notation: /plantae/:genus.:species → { genus: string; species: string }\n * - Regex constraints: /user/:id(\\\\d+) → { id: string }\n * - Optional parameters: /posts/:year/:month? → { year: string; month?: string }\n * - Wildcard parameters: /files/* → { \"0\": string }\n * - Multiple wildcards: /a/star/b/star → { \"0\": string; \"1\": string }\n */\nexport type ExtractRouteParams<Path extends string> = string extends Path\n ? Record<string, string>\n : ExtractParams<Path>;\n\n/**\n * Main parameter extraction logic - enhanced for Express 5 support\n */\ntype ExtractParams<Path extends string> =\n // Handle Express 5 braces for optional segments: {/:param} or {/path/:param}\n Path extends `${infer Before}{${infer OptionalContent}}${infer After}`\n ? ExtractOptionalSegment<OptionalContent> &\n ExtractParams<`${Before}${After}`>\n : // Handle named parameters :paramName\n Path extends `${infer _Before}:${infer Rest}`\n ? ExtractSingleParam<Rest> & ExtractParams<RemoveFirstParam<Path>>\n : // Handle wildcards *\n Path extends `${infer _Before}*${infer After}`\n ? { [K in CountWildcards<_Before>]: string } & ExtractParams<After>\n : // No more parameters\n {};\n\n/**\n * Extract parameters from Express 5 optional segments in braces\n * Handles patterns like {/:param}, {.:ext}, {/optional/:param}\n */\ntype ExtractOptionalSegment<Content extends string> =\n // Handle optional parameter patterns like /:param\n Content extends `/:${infer Rest}`\n ? ExtractOptionalParam<Rest>\n : Content extends `.:${infer Rest}`\n ? ExtractOptionalParam<Rest>\n : Content extends `${infer _Path}:${infer Rest}`\n ? ExtractOptionalParam<Rest>\n : {};\n\n/**\n * Extract a single optional parameter from brace content\n */\ntype ExtractOptionalParam<Rest extends string> =\n Rest extends `${infer ParamName}/${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}-${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}.${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}`\n ? { [K in ParamName]?: string }\n : {};\n\n/**\n * Extract a single parameter name from the rest of the path\n * Enhanced to handle Express 5 patterns and optional parameters correctly\n * Special handling for consecutive parameters like :from-:to\n * Order matters: regex constraints must be handled before repeating parameters\n */\ntype ExtractSingleParam<Rest extends string> =\n // Handle regex constraints FIRST (before +, *, ?) to avoid conflicts\n Rest extends `${infer ParamName}(${infer _Constraint})${infer _After}`\n ? { [K in ParamName]: string } // Handle consecutive parameters with separators first: param-:nextParam\n : Rest extends `${infer ParamName}-:${infer _NextParam}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}.:${infer _NextParam}`\n ? { [K in ParamName]: string }\n : // Handle optional parameters followed by delimiters (before regular delimiters)\n Rest extends `${infer ParamName}?/${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?-${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?.${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?#${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?:${infer _After}`\n ? { [K in ParamName]?: string }\n : // Then handle regular delimiters (after optional parameter patterns)\n Rest extends `${infer ParamName}/${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}-${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}.${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}#${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}:${infer _After}`\n ? { [K in ParamName]: string }\n : // Handle Express 5 repeating parameters (after regular delimiters)\n Rest extends `${infer ParamName}+${infer _After}`\n ? { [K in ParamName]: string[] }\n : Rest extends `${infer ParamName}*${infer _After}`\n ? { [K in ParamName]?: string[] }\n : // Handle optional parameters with ? (Express 4) - only at the end of a segment\n Rest extends `${infer ParamName}?${infer _After}`\n ? { [K in ParamName]?: string } // Parameter at absolute end of string\n : Rest extends string\n ? Rest extends \"\"\n ? {}\n : Rest extends `${infer ParamName}?`\n ? { [K in ParamName]?: string } // ParamName here doesn't include the ?\n : Rest extends `${infer ParamName}+`\n ? { [K in ParamName]: string[] }\n : Rest extends `${infer ParamName}*`\n ? { [K in ParamName]?: string[] }\n : { [K in Rest]: string }\n : {};\n\n/**\n * Remove the first parameter from path to continue parsing\n * Enhanced to handle Express 5 patterns and optional parameters\n * Handles patterns like :from-:to by removing just :from and keeping -:to\n * Order matters: regex constraints must be handled before repeating parameters\n */\ntype RemoveFirstParam<Path extends string> =\n Path extends `${infer Before}:${infer Rest}`\n ? // Handle regex constraints FIRST (before +, *, ?) to avoid conflicts\n Rest extends `${infer _ParamName}(${infer _Constraint})${infer After}`\n ? `${Before}${After}` // Handle consecutive parameters: :param-:nextParam -> -:nextParam\n : Rest extends `${infer _ParamName}-:${infer After}`\n ? `${Before}-:${After}`\n : Rest extends `${infer _ParamName}.:${infer After}`\n ? `${Before}.:${After}`\n : // Handle optional parameters followed by delimiters (before regular delimiters)\n Rest extends `${infer _ParamName}?/${infer After}`\n ? `${Before}/${After}`\n : Rest extends `${infer _ParamName}?-${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}?.${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}?#${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}?:${infer After}`\n ? `${Before}:${After}`\n : // Handle regular separators (after optional parameter patterns)\n Rest extends `${infer _ParamName}/${infer After}`\n ? `${Before}/${After}`\n : Rest extends `${infer _ParamName}-${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}.${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}#${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}:${infer After}`\n ? `${Before}:${After}`\n : // Handle Express 5 repeating parameters (after regular separators)\n Rest extends `${infer _ParamName}+${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}*${infer After}`\n ? `${Before}${After}`\n : // Handle optional parameters with ?\n Rest extends `${infer _ParamName}?${infer After}`\n ? `${Before}${After}`\n : Before\n : Path;\n\n/**\n * Count wildcards to assign proper numeric indices\n */\ntype CountWildcards<\n Path extends string,\n Count extends string = \"0\"\n> = Path extends `${infer _Before}*${infer Rest}`\n ? CountWildcards<Rest, IncrementWildcard<Count>>\n : Count;\n\n/**\n * Helper type to increment wildcard count as string\n */\ntype IncrementWildcard<T extends string> = T extends \"0\"\n ? \"1\"\n : T extends \"1\"\n ? \"2\"\n : T extends \"2\"\n ? \"3\"\n : T extends \"3\"\n ? \"4\"\n : T extends \"4\"\n ? \"5\"\n : T extends \"5\"\n ? \"6\"\n : T extends \"6\"\n ? \"7\"\n : T extends \"7\"\n ? \"8\"\n : T extends \"8\"\n ? \"9\"\n : \"10\"; // Reasonable limit for wildcards\n\n/**\n * Express middleware that adds custom properties to the request object and/or response locals.\n *\n * @template TReq - The shape of the properties added to the request object.\n * @template TLocals - The shape of the properties added to response.locals.\n * @param req - The Express request object, extended with TReq.\n * @param res - The Express response object with typed locals.\n * @param next - The next middleware function.\n */\nexport type TypedMiddleware<\n TReq extends Record<string, any> = {},\n TLocals extends Record<string, any> = {}\n> = (\n req: Request & TReq,\n res: Response<any, TLocals>,\n next: NextFunction\n) => void | Promise<void>;\n\n/**\n * Simplified TypedMiddleware for request-only extensions (backward compatibility)\n */\nexport type RequestOnlyMiddleware<TReq extends Record<string, any>> =\n TypedMiddleware<TReq, {}>;\n\n/**\n * Simplified TypedMiddleware for response locals-only extensions\n */\nexport type LocalsOnlyMiddleware<TLocals extends Record<string, any>> =\n TypedMiddleware<{}, TLocals>;\n\n// Utility type to infer props from middleware array\ntype InferMiddlewareProps<T extends readonly TypedMiddleware<any, any>[]> =\n T extends readonly [infer First, ...infer Rest]\n ? First extends TypedMiddleware<infer FirstReq, any>\n ? Rest extends readonly TypedMiddleware<any, any>[]\n ? FirstReq & InferMiddlewareProps<Rest>\n : FirstReq\n : {}\n : {};\n\n// Utility type to infer locals from middleware array\ntype InferMiddlewareLocals<T extends readonly TypedMiddleware<any, any>[]> =\n T extends readonly [infer First, ...infer Rest]\n ? First extends TypedMiddleware<any, infer FirstLocals>\n ? Rest extends readonly TypedMiddleware<any, any>[]\n ? FirstLocals & InferMiddlewareLocals<Rest>\n : FirstLocals\n : {}\n : {};\n\n// Enhanced Request type with proper inference\nexport type ZodRequest<\n Path extends string = string,\n BodySchema extends ZodType<any, any, any> | unknown = unknown,\n QuerySchema extends ZodType<any, any, any> | unknown = unknown,\n MiddlewareProps extends Record<string, any> = {}\n> = Omit<Request, \"params\" | \"query\" | \"body\"> & {\n params: ExtractRouteParams<Path>;\n body: BodySchema extends ZodType<any, any, any>\n ? z.infer<BodySchema>\n : unknown;\n query: QuerySchema extends ZodType<any, any, any>\n ? z.infer<QuerySchema>\n : unknown;\n} & MiddlewareProps;\n\n// Route handler type\nexport type ZodRouteHandler<\n Path extends string = string,\n BodySchema extends ZodType<any, any, any> | unknown = unknown,\n QuerySchema extends ZodType<any, any, any> | unknown = unknown,\n MiddlewareProps extends Record<string, any> = {},\n ResponseLocals extends Record<string, any> = {}\n> = (\n req: ZodRequest<Path, BodySchema, QuerySchema, MiddlewareProps>,\n res: Response<any, ResponseLocals>,\n next?: NextFunction\n) => void | Promise<void> | Response | Promise<Response>;\n\n/**\n * Options for defining a typed route, including schemas and middleware.\n *\n * @template BodySchema - Zod schema for request body validation.\n * @template QuerySchema - Zod schema for query parameter validation.\n * @property bodySchema - Optional Zod schema for validating the request body.\n * @property querySchema - Optional Zod schema for validating the query string.\n * @property middleware - Optional array of TypedMiddleware for this route.\n */\nexport interface RouteOptions<\n BodySchema extends ZodType<any, any, any> | unknown = unknown,\n QuerySchema extends ZodType<any, any, any> | unknown = unknown\n> {\n bodySchema?: BodySchema extends ZodType<any, any, any> ? BodySchema : never;\n querySchema?: QuerySchema extends ZodType<any, any, any>\n ? QuerySchema\n : never;\n middleware?: readonly TypedMiddleware<any, any>[];\n}\n\n// HTTP methods\nexport type HttpMethod =\n | \"get\"\n | \"post\"\n | \"put\"\n | \"delete\"\n | \"patch\"\n | \"options\"\n | \"head\"\n | \"all\";\n\n// Main typed router class\nclass TypedRouter<\n RouterMiddlewareProps extends Record<string, any> = {},\n RouterLocals extends Record<string, any> = {}\n> {\n private router: express.Router;\n\n constructor() {\n this.router = express.Router();\n }\n /**\n * Add typed middleware that extends the request with additional properties\n * and/or adds properties to response.locals\n */\n useTypedMiddleware<\n TReq extends Record<string, any> = {},\n TLocals extends Record<string, any> = {}\n >(\n middleware: TypedMiddleware<TReq, TLocals>\n ): TypedRouter<RouterMiddlewareProps & TReq, RouterLocals & TLocals> {\n this.router.use(middleware as any);\n return this as any;\n }\n /**\n * Get the underlying Express router\n */\n getRouter(): express.Router {\n return this.router;\n }\n // Method overloads for GET requests with automatic middleware type inference\n get<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n get<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Special overload for middleware type inference\n get<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Combined overload for body/query schema + middleware\n get<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema> & { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n // Implementation\n get(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"get\", path, optionsOrHandler, handler);\n }\n // Combined overload for body/query schema + middleware (most specific first)\n post<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body schema only + middleware\n post<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n post<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body + Query schema without middleware\n post<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Just handler, no options\n post<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n post(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"post\", path, optionsOrHandler, handler);\n }\n // PUT method with all the same overloads as POST\n put<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n put(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"put\", path, optionsOrHandler, handler);\n }\n // PATCH method with all the same overloads as POST\n patch<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n patch(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"patch\", path, optionsOrHandler, handler);\n } // DELETE method (typically no body, but can have query params and middleware)\n // Most specific first: query schema + middleware\n delete<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema only\n delete<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: { querySchema: QuerySchema },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n delete<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n delete<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n delete(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"delete\", path, optionsOrHandler, handler);\n } // OPTIONS method (typically no body, used for CORS preflight)\n // Most specific first: query schema + middleware\n options<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema only\n options<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: { querySchema: QuerySchema },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n options<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n options<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n options(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"options\", path, optionsOrHandler, handler);\n } // HEAD method (like GET but only returns headers)\n // Most specific first: query schema + middleware\n head<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema only\n head<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: { querySchema: QuerySchema },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n head<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n head<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n head(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"head\", path, optionsOrHandler, handler);\n } // ALL method (matches all HTTP methods)\n // Most specific first: body + query + middleware\n all<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body schema + middleware (no query)\n all<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema + middleware (no body)\n all<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body + query schemas (no middleware)\n all<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only (no schemas)\n all<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n all<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n all(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"all\", path, optionsOrHandler, handler);\n }\n // Helper method to register routes\n private registerRoute(\n method: HttpMethod,\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n const middlewares: any[] = [];\n\n if (typeof optionsOrHandler === \"object\") {\n const options = optionsOrHandler as RouteOptions<any, any>;\n\n // Add per-route middleware first\n if (options.middleware) {\n middlewares.push(...options.middleware);\n }\n\n // Add schema validation middleware\n if (options.bodySchema) {\n middlewares.push(\n this.createBodyValidationMiddleware(options.bodySchema)\n );\n }\n if (options.querySchema) {\n middlewares.push(\n this.createQueryValidationMiddleware(options.querySchema)\n );\n }\n\n // Add the main handler\n middlewares.push(handler);\n } else {\n // Direct handler without options\n middlewares.push(optionsOrHandler);\n }\n\n // Register with Express router\n (this.router as any)[method](path, ...middlewares);\n\n return this;\n }\n\n private createBodyValidationMiddleware(schema: ZodType<any, any, any>) {\n return (req: Request, res: Response, next: NextFunction) => {\n try {\n req.body = schema.parse(req.body);\n next();\n } catch (error) {\n if (error instanceof z.ZodError) {\n res.status(400).json({\n error: \"Validation failed\",\n details: error.errors,\n });\n } else {\n next(error);\n }\n }\n };\n }\n\n private createQueryValidationMiddleware(schema: ZodType<any, any, any>) {\n return (req: Request, res: Response, next: NextFunction) => {\n try {\n req.query = schema.parse(req.query);\n next();\n } catch (error) {\n if (error instanceof z.ZodError) {\n res.status(400).json({\n error: \"Validation failed\",\n details: error.errors,\n });\n } else {\n next(error);\n }\n }\n };\n }\n}\n\n/**\n * Create a new strongly-typed Express router instance.\n *\n * This is the simplest way to get started with @minisylar/express-typed-router.\n *\n * @example\n * import { createTypedRouter } from '@minisylar/express-typed-router';\n *\n * // Create a router and add a typed GET route\n * const router = createTypedRouter();\n * router.get('/hello/:name', (req, res) => {\n * // req.params.name is typed as string\n * res.json({ message: `Hello, ${req.params.name}!` });\n * });\n *\n * // Use with Express\n * import express from 'express';\n * const app = express();\n * app.use('/api', router.getRouter());\n */\nexport function createTypedRouter<\n RouterMiddlewareProps extends Record<string, any> = {},\n RouterLocals extends Record<string, any> = {}\n>(): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return new TypedRouter<RouterMiddlewareProps, RouterLocals>();\n}\n\n// Option 2: Factory with optional configuration\n\n/**\n * Configuration options for createTypedRouterWithConfig.\n *\n * @property validateInput - (Future) Whether to enable global input validation.\n * @property errorHandler - Optional global error handler middleware for the router.\n */\nexport interface RouterConfig {\n validateInput?: boolean;\n errorHandler?: (\n error: any,\n req: Request,\n res: Response,\n next: NextFunction\n ) => void;\n}\n\n/**\n * Create a new typed router with optional configuration.\n *\n * Use this if you want to add a global error handler or future global options.\n *\n * @param config - Optional configuration for the router (e.g. error handler).\n * @returns A new TypedRouter instance.\n *\n * @example\n * import { createTypedRouterWithConfig } from '@minisylar/express-typed-router';\n *\n * const router = createTypedRouterWithConfig({\n * errorHandler: (err, req, res, next) => {\n * res.status(500).json({ error: 'Something went wrong', details: err });\n * }\n * });\n */\nexport function createTypedRouterWithConfig<\n RouterMiddlewareProps extends Record<string, any> = {},\n RouterLocals extends Record<string, any> = {}\n>(config?: RouterConfig): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n const router = new TypedRouter<RouterMiddlewareProps, RouterLocals>();\n if (config?.errorHandler) {\n router.getRouter().use(config.errorHandler);\n }\n return router;\n}\n\n// Option 3: Factory with pre-configured middleware\n\n/**\n * Create a new typed router with pre-configured middleware.\n *\n * This is useful for setting up router-level middleware in a single call.\n *\n * @param middleware - One or more TypedMiddleware functions to apply to all routes.\n * @returns A new TypedRouter instance with the middleware applied.\n *\n * @example\n * import { createTypedRouterWithMiddleware } from '@minisylar/express-typed-router';\n *\n * const router = createTypedRouterWithMiddleware(authMiddleware, loggingMiddleware);\n */\nexport function createTypedRouterWithMiddleware<T extends Record<string, any>>(\n ...middleware: TypedMiddleware<any, any>[]\n): TypedRouter<T> {\n let router = new TypedRouter() as any;\n for (const mw of middleware) {\n router = router.useTypedMiddleware(mw);\n }\n return router;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"zod-router.cjs","names":["middleware: TypedMiddleware<TReq, TLocals>","path: string","optionsOrHandler: any","handler?: any","method: HttpMethod","middlewares: any[]","schema: ZodType<any, any, any>","req: Request","res: Response","next: NextFunction","z","config?: RouterConfig"],"sources":["../src/zod-router.ts"],"sourcesContent":["/**\n * @packageDocumentation\n * @module @minisylar/express-typed-router\n *\n * @title @minisylar/express-typed-router\n *\n * A strongly-typed Express router with Zod validation and automatic type inference for params, body, query, and middleware.\n *\n * @example\n * // Example 1: Basic usage with router-level middleware\n * const router = createTypedRouter()\n * .useMiddleware(timestampMiddleware)\n * .useMiddleware(requestIdMiddleware)\n *\n * router.get('/posts/:postId', (req, res) => {\n * const { postId } = req.params // Typed as { postId: string }\n * const { timestamp, requestId } = req // Both properties are now typed correctly!\n * res.json({ postId, timestamp, requestId })\n * })\n *\n * @example\n * // Example 2: Per-route middleware with automatic type inference\n * router.post(\n * '/posts',\n * {\n * bodySchema: CreatePostSchema,\n * middleware: [timestampMiddleware, requestIdMiddleware] as const\n * },\n * (req, res) => {\n * const { title, content, tags } = req.body // From schema validation\n * const { timestamp, requestId } = req // From middleware - should be automatically typed!\n * res.json({ title, content, tags, timestamp, requestId })\n * }\n * )\n *\n * @example\n * // Example 3: Mixed middleware\n * const router = createTypedRouter().useMiddleware(requestIdMiddleware)\n * router.get(\n * '/posts/:postId',\n * {\n * middleware: [authMiddleware] as const\n * },\n * (req, res) => {\n * const { postId } = req.params\n * const { requestId } = req // From router-level middleware\n * const { userId, hasPermission } = req // From per-route middleware\n * res.json({ postId, requestId, userId, hasPermission })\n * }\n * )\n *\n * @example\n * // Example 4: Using factory with pre-configured middleware\n * const router = createTypedRouterWithMiddleware(timestampMiddleware, requestIdMiddleware)\n * router.get('/simple/:id', (req, res) => {\n * const { id } = req.params\n * const { timestamp, requestId } = req // Already available from factory setup!\n * res.json({ id, timestamp, requestId })\n * })\n *\n * @example\n * // Example 5: Demonstrating all HTTP methods\n * const router = createTypedRouter().useMiddleware(requestIdMiddleware)\n *\n * // GET with query validation\n * router.get('/posts', { querySchema: QuerySchema }, (req, res) => {\n * const { limit, offset } = req.query // Typed from schema\n * const { requestId } = req // From router middleware\n * res.json({ posts: [], limit, offset, requestId })\n * })\n *\n * // POST with body validation and middleware\n * router.post(\n * '/posts',\n * {\n * bodySchema: CreatePostSchema,\n * middleware: [timestampMiddleware] as const\n * },\n * (req, res) => {\n * const { title, content } = req.body // From body schema\n * const { requestId, timestamp } = req // From middleware\n * res.json({ id: 'new-post', title, content, requestId, timestamp })\n * }\n * )\n *\n * // PUT for full updates\n * router.put(\n * '/posts/:postId',\n * {\n * bodySchema: CreatePostSchema,\n * middleware: [authMiddleware] as const\n * },\n * (req, res) => {\n * const { postId } = req.params\n * const { title, content } = req.body\n * const { requestId, userId, hasPermission } = req\n * res.json({ postId, title, content, requestId, userId, hasPermission })\n * }\n * )\n *\n * // PATCH for partial updates\n * router.patch('/posts/:postId', { bodySchema: UpdatePostSchema }, (req, res) => {\n * const { postId } = req.params\n * const updates = req.body // Partial update object\n * const { requestId } = req\n * res.json({ postId, updates, requestId })\n * })\n *\n * // DELETE\n * router.delete('/posts/:postId', (req, res) => {\n * const { postId } = req.params\n * const { requestId } = req\n * res.json({ deleted: postId, requestId })\n * })\n *\n * // OPTIONS for CORS preflight\n * router.options('/posts/*', (req, res) => {\n * res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE')\n * res.header('Access-Control-Allow-Headers', 'Content-Type')\n * res.status(200).end()\n * })\n *\n * // HEAD for metadata only\n * router.head('/posts/:postId', (req, res) => {\n * const { postId } = req.params\n * res.header('X-Post-ID', postId)\n * res.status(200).end()\n * })\n *\n * // ALL method for catch-all routes\n * router.all('/debug/*', (req, res) => {\n * const { requestId } = req\n * res.json({\n * method: req.method,\n * path: req.path,\n * requestId\n * })\n * })\n */\n/* eslint-disable @typescript-eslint/no-empty-object-type */\nimport express, {\n type Request,\n type Response,\n type NextFunction,\n} from \"express\";\nimport { z, type ZodType } from \"zod\";\n\n/**\n * Extract route parameters from Express.js route patterns.\n *\n * Supports all Express.js routing patterns:\n * - Named parameters: /users/:userId → { userId: string }\n * - Multiple parameters: /users/:userId/books/:bookId → { userId: string; bookId: string }\n * - Parameters with separators: /flights/:from-:to → { from: string; to: string }\n * - Dot notation: /plantae/:genus.:species → { genus: string; species: string }\n * - Regex constraints: /user/:id(\\\\d+) → { id: string }\n * - Optional parameters: /posts/:year/:month? → { year: string; month?: string }\n * - Wildcard parameters: /files/* → { \"0\": string }\n * - Multiple wildcards: /a/star/b/star → { \"0\": string; \"1\": string }\n */\nexport type ExtractRouteParams<Path extends string> = string extends Path\n ? Record<string, string>\n : ExtractParams<Path>;\n\n/**\n * Main parameter extraction logic - enhanced for Express 5 support\n */\ntype ExtractParams<Path extends string> =\n // Handle Express 5 braces for optional segments: {/:param} or {/path/:param}\n Path extends `${infer Before}{${infer OptionalContent}}${infer After}`\n ? ExtractOptionalSegment<OptionalContent> &\n ExtractParams<`${Before}${After}`>\n : // Handle named parameters :paramName\n Path extends `${infer _Before}:${infer Rest}`\n ? ExtractSingleParam<Rest> & ExtractParams<RemoveFirstParam<Path>>\n : // Handle wildcards *\n Path extends `${infer _Before}*${infer After}`\n ? { [K in CountWildcards<_Before>]: string } & ExtractParams<After>\n : // No more parameters\n {};\n\n/**\n * Extract parameters from Express 5 optional segments in braces\n * Handles patterns like {/:param}, {.:ext}, {/optional/:param}\n */\ntype ExtractOptionalSegment<Content extends string> =\n // Handle optional parameter patterns like /:param\n Content extends `/:${infer Rest}`\n ? ExtractOptionalParam<Rest>\n : Content extends `.:${infer Rest}`\n ? ExtractOptionalParam<Rest>\n : Content extends `${infer _Path}:${infer Rest}`\n ? ExtractOptionalParam<Rest>\n : {};\n\n/**\n * Extract a single optional parameter from brace content\n */\ntype ExtractOptionalParam<Rest extends string> =\n Rest extends `${infer ParamName}/${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}-${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}.${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}`\n ? { [K in ParamName]?: string }\n : {};\n\n/**\n * Extract a single parameter name from the rest of the path\n * Enhanced to handle Express 5 patterns and optional parameters correctly\n * Special handling for consecutive parameters like :from-:to\n * Order matters: regex constraints must be handled before repeating parameters\n */\ntype ExtractSingleParam<Rest extends string> =\n // Handle regex constraints FIRST (before +, *, ?) to avoid conflicts\n Rest extends `${infer ParamName}(${infer _Constraint})${infer _After}`\n ? { [K in ParamName]: string } // Handle consecutive parameters with separators first: param-:nextParam\n : Rest extends `${infer ParamName}-:${infer _NextParam}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}.:${infer _NextParam}`\n ? { [K in ParamName]: string }\n : // Handle optional parameters followed by delimiters (before regular delimiters)\n Rest extends `${infer ParamName}?/${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?-${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?.${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?#${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?:${infer _After}`\n ? { [K in ParamName]?: string }\n : // Then handle regular delimiters (after optional parameter patterns)\n Rest extends `${infer ParamName}/${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}-${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}.${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}#${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}:${infer _After}`\n ? { [K in ParamName]: string }\n : // Handle Express 5 repeating parameters (after regular delimiters)\n Rest extends `${infer ParamName}+${infer _After}`\n ? { [K in ParamName]: string[] }\n : Rest extends `${infer ParamName}*${infer _After}`\n ? { [K in ParamName]?: string[] }\n : // Handle optional parameters with ? (Express 4) - only at the end of a segment\n Rest extends `${infer ParamName}?${infer _After}`\n ? { [K in ParamName]?: string } // Parameter at absolute end of string\n : Rest extends string\n ? Rest extends \"\"\n ? {}\n : Rest extends `${infer ParamName}?`\n ? { [K in ParamName]?: string } // ParamName here doesn't include the ?\n : Rest extends `${infer ParamName}+`\n ? { [K in ParamName]: string[] }\n : Rest extends `${infer ParamName}*`\n ? { [K in ParamName]?: string[] }\n : { [K in Rest]: string }\n : {};\n\n/**\n * Remove the first parameter from path to continue parsing\n * Enhanced to handle Express 5 patterns and optional parameters\n * Handles patterns like :from-:to by removing just :from and keeping -:to\n * Order matters: regex constraints must be handled before repeating parameters\n */\ntype RemoveFirstParam<Path extends string> =\n Path extends `${infer Before}:${infer Rest}`\n ? // Handle regex constraints FIRST (before +, *, ?) to avoid conflicts\n Rest extends `${infer _ParamName}(${infer _Constraint})${infer After}`\n ? `${Before}${After}` // Handle consecutive parameters: :param-:nextParam -> -:nextParam\n : Rest extends `${infer _ParamName}-:${infer After}`\n ? `${Before}-:${After}`\n : Rest extends `${infer _ParamName}.:${infer After}`\n ? `${Before}.:${After}`\n : // Handle optional parameters followed by delimiters (before regular delimiters)\n Rest extends `${infer _ParamName}?/${infer After}`\n ? `${Before}/${After}`\n : Rest extends `${infer _ParamName}?-${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}?.${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}?#${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}?:${infer After}`\n ? `${Before}:${After}`\n : // Handle regular separators (after optional parameter patterns)\n Rest extends `${infer _ParamName}/${infer After}`\n ? `${Before}/${After}`\n : Rest extends `${infer _ParamName}-${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}.${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}#${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}:${infer After}`\n ? `${Before}:${After}`\n : // Handle Express 5 repeating parameters (after regular separators)\n Rest extends `${infer _ParamName}+${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}*${infer After}`\n ? `${Before}${After}`\n : // Handle optional parameters with ?\n Rest extends `${infer _ParamName}?${infer After}`\n ? `${Before}${After}`\n : Before\n : Path;\n\n/**\n * Count wildcards to assign proper numeric indices\n */\ntype CountWildcards<\n Path extends string,\n Count extends string = \"0\"\n> = Path extends `${infer _Before}*${infer Rest}`\n ? CountWildcards<Rest, IncrementWildcard<Count>>\n : Count;\n\n/**\n * Helper type to increment wildcard count as string\n */\ntype IncrementWildcard<T extends string> = T extends \"0\"\n ? \"1\"\n : T extends \"1\"\n ? \"2\"\n : T extends \"2\"\n ? \"3\"\n : T extends \"3\"\n ? \"4\"\n : T extends \"4\"\n ? \"5\"\n : T extends \"5\"\n ? \"6\"\n : T extends \"6\"\n ? \"7\"\n : T extends \"7\"\n ? \"8\"\n : T extends \"8\"\n ? \"9\"\n : \"10\"; // Reasonable limit for wildcards\n\n/**\n * Express middleware that adds custom properties to the request object and/or response locals.\n *\n * @template TReq - The shape of the properties added to the request object.\n * @template TLocals - The shape of the properties added to response.locals.\n * @param req - The Express request object, extended with TReq.\n * @param res - The Express response object with typed locals.\n * @param next - The next middleware function.\n */\nexport type TypedMiddleware<\n TReq extends Record<string, any> = {},\n TLocals extends Record<string, any> = {}\n> = (\n req: Request & TReq,\n res: Response<any, TLocals>,\n next: NextFunction\n) => void | Promise<void>;\n\n/**\n * Simplified TypedMiddleware for request-only extensions (backward compatibility)\n */\nexport type RequestOnlyMiddleware<TReq extends Record<string, any>> =\n TypedMiddleware<TReq, {}>;\n\n/**\n * Simplified TypedMiddleware for response locals-only extensions\n */\nexport type LocalsOnlyMiddleware<TLocals extends Record<string, any>> =\n TypedMiddleware<{}, TLocals>;\n\n// Utility type to infer props from middleware array\ntype InferMiddlewareProps<T extends readonly TypedMiddleware<any, any>[]> =\n T extends readonly [infer First, ...infer Rest]\n ? First extends TypedMiddleware<infer FirstReq, any>\n ? Rest extends readonly TypedMiddleware<any, any>[]\n ? FirstReq & InferMiddlewareProps<Rest>\n : FirstReq\n : {}\n : {};\n\n// Utility type to infer locals from middleware array\ntype InferMiddlewareLocals<T extends readonly TypedMiddleware<any, any>[]> =\n T extends readonly [infer First, ...infer Rest]\n ? First extends TypedMiddleware<any, infer FirstLocals>\n ? Rest extends readonly TypedMiddleware<any, any>[]\n ? FirstLocals & InferMiddlewareLocals<Rest>\n : FirstLocals\n : {}\n : {};\n\n// Enhanced Request type with proper inference\nexport type ZodRequest<\n Path extends string = string,\n BodySchema extends ZodType<any, any, any> | unknown = unknown,\n QuerySchema extends ZodType<any, any, any> | unknown = unknown,\n MiddlewareProps extends Record<string, any> = {}\n> = Omit<Request, \"params\" | \"query\" | \"body\"> & {\n params: ExtractRouteParams<Path>;\n body: BodySchema extends ZodType<any, any, any>\n ? z.infer<BodySchema>\n : unknown;\n query: QuerySchema extends ZodType<any, any, any>\n ? z.infer<QuerySchema>\n : unknown;\n} & MiddlewareProps;\n\n// Route handler type\nexport type ZodRouteHandler<\n Path extends string = string,\n BodySchema extends ZodType<any, any, any> | unknown = unknown,\n QuerySchema extends ZodType<any, any, any> | unknown = unknown,\n MiddlewareProps extends Record<string, any> = {},\n ResponseLocals extends Record<string, any> = {}\n> = (\n req: ZodRequest<Path, BodySchema, QuerySchema, MiddlewareProps>,\n res: Response<any, ResponseLocals>,\n next?: NextFunction\n) => void | Promise<void> | Response | Promise<Response>;\n\n/**\n * Options for defining a typed route, including schemas and middleware.\n *\n * @template BodySchema - Zod schema for request body validation.\n * @template QuerySchema - Zod schema for query parameter validation.\n * @property bodySchema - Optional Zod schema for validating the request body.\n * @property querySchema - Optional Zod schema for validating the query string.\n * @property middleware - Optional array of TypedMiddleware for this route.\n */\nexport interface RouteOptions<\n BodySchema extends ZodType<any, any, any> | unknown = unknown,\n QuerySchema extends ZodType<any, any, any> | unknown = unknown\n> {\n bodySchema?: BodySchema extends ZodType<any, any, any> ? BodySchema : never;\n querySchema?: QuerySchema extends ZodType<any, any, any>\n ? QuerySchema\n : never;\n middleware?: TypedMiddleware<any, any>[];\n}\n\n// HTTP methods\nexport type HttpMethod =\n | \"get\"\n | \"post\"\n | \"put\"\n | \"delete\"\n | \"patch\"\n | \"options\"\n | \"head\"\n | \"all\";\n\n// Main typed router class\nclass TypedRouter<\n RouterMiddlewareProps extends Record<string, any> = {},\n RouterLocals extends Record<string, any> = {}\n> {\n private router: express.Router;\n\n constructor() {\n this.router = express.Router();\n }\n /**\n * Add typed middleware that extends the request with additional properties\n * and/or adds properties to response.locals\n */ /**\n * Add typed middleware to the router.\n * This middleware will apply to all routes defined after this call.\n *\n * @template TReq - Type extensions for the request object\n * @template TLocals - Type extensions for response.locals\n * @param middleware - The typed middleware function\n * @returns A new router instance with updated types\n */\n useMiddleware<\n TReq extends Record<string, any> = {},\n TLocals extends Record<string, any> = {}\n >(\n middleware: TypedMiddleware<TReq, TLocals>\n ): TypedRouter<RouterMiddlewareProps & TReq, RouterLocals & TLocals> {\n this.router.use(middleware as any);\n return this as any;\n }\n /**\n * Get the underlying Express router\n */\n getRouter(): express.Router {\n return this.router;\n }\n // Method overloads for GET requests with automatic middleware type inference\n get<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n get<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Special overload for middleware type inference\n get<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n // Combined overload for body/query schema + middleware\n get<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown,\n M extends TypedMiddleware<any, any>[] // Using array type for JS compatibility\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema> & { middleware: [...M] }, // Using tuple spread pattern\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference\n RouterLocals & InferMiddlewareLocals<readonly [...M]>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n // Implementation\n get(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"get\", path, optionsOrHandler, handler);\n } // Combined overload for body/query schema + middleware (most specific first)\n post<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n M extends TypedMiddleware<any, any>[] // Using array type for JS compatibility\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: [...M]; // Using tuple spread pattern\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference\n RouterLocals & InferMiddlewareLocals<readonly [...M]>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body schema only + middleware\n post<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n M extends TypedMiddleware<any, any>[] // Using array type for JS compatibility\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: [...M] }, // Using tuple spread pattern\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference\n RouterLocals & InferMiddlewareLocals<readonly [...M]>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n post<\n Path extends string,\n M extends TypedMiddleware<any, any>[] // Using array type for JS compatibility\n >(\n path: Path,\n options: { middleware: [...M] }, // Using tuple spread pattern\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference\n RouterLocals & InferMiddlewareLocals<readonly [...M]>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body + Query schema without middleware\n post<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Just handler, no options\n post<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n post(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"post\", path, optionsOrHandler, handler);\n }\n // PUT method with all the same overloads as POST\n put<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n put(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"put\", path, optionsOrHandler, handler);\n }\n // PATCH method with all the same overloads as POST\n patch<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n patch(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"patch\", path, optionsOrHandler, handler);\n } // DELETE method (typically no body, but can have query params and middleware)\n // Most specific first: query schema + middleware\n delete<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema only\n delete<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: { querySchema: QuerySchema },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n delete<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n delete<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n delete(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"delete\", path, optionsOrHandler, handler);\n } // OPTIONS method (typically no body, used for CORS preflight)\n // Most specific first: query schema + middleware\n options<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema only\n options<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: { querySchema: QuerySchema },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n options<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n options<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n options(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"options\", path, optionsOrHandler, handler);\n } // HEAD method (like GET but only returns headers)\n // Most specific first: query schema + middleware\n head<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema only\n head<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: { querySchema: QuerySchema },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n head<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n head<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n head(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"head\", path, optionsOrHandler, handler);\n } // ALL method (matches all HTTP methods)\n // Most specific first: body + query + middleware\n all<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body schema + middleware (no query)\n all<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema + middleware (no body)\n all<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body + query schemas (no middleware)\n all<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only (no schemas)\n all<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n all<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n all(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"all\", path, optionsOrHandler, handler);\n }\n // Helper method to register routes\n private registerRoute(\n method: HttpMethod,\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n const middlewares: any[] = [];\n\n if (typeof optionsOrHandler === \"object\") {\n const options = optionsOrHandler as RouteOptions<any, any>;\n\n // Add per-route middleware first\n if (options.middleware) {\n middlewares.push(...options.middleware);\n }\n\n // Add schema validation middleware\n if (options.bodySchema) {\n middlewares.push(\n this.createBodyValidationMiddleware(options.bodySchema)\n );\n }\n if (options.querySchema) {\n middlewares.push(\n this.createQueryValidationMiddleware(options.querySchema)\n );\n }\n\n // Add the main handler\n middlewares.push(handler);\n } else {\n // Direct handler without options\n middlewares.push(optionsOrHandler);\n }\n\n // Register with Express router\n (this.router as any)[method](path, ...middlewares);\n\n return this;\n }\n\n private createBodyValidationMiddleware(schema: ZodType<any, any, any>) {\n return (req: Request, res: Response, next: NextFunction) => {\n try {\n req.body = schema.parse(req.body);\n next();\n } catch (error) {\n if (error instanceof z.ZodError) {\n res.status(400).json({\n error: \"Validation failed\",\n details: error.errors,\n });\n } else {\n next(error);\n }\n }\n };\n }\n\n private createQueryValidationMiddleware(schema: ZodType<any, any, any>) {\n return (req: Request, res: Response, next: NextFunction) => {\n try {\n req.query = schema.parse(req.query);\n next();\n } catch (error) {\n if (error instanceof z.ZodError) {\n res.status(400).json({\n error: \"Validation failed\",\n details: error.errors,\n });\n } else {\n next(error);\n }\n }\n };\n }\n}\n\n/**\n * Create a new strongly-typed Express router instance.\n *\n * This is the simplest way to get started with @minisylar/express-typed-router.\n *\n * @example\n * import { createTypedRouter } from '@minisylar/express-typed-router';\n *\n * // Create a router and add a typed GET route\n * const router = createTypedRouter();\n * router.get('/hello/:name', (req, res) => {\n * // req.params.name is typed as string\n * res.json({ message: `Hello, ${req.params.name}!` });\n * });\n *\n * // Use with Express\n * import express from 'express';\n * const app = express();\n * app.use('/api', router.getRouter());\n */\nexport function createTypedRouter<\n RouterMiddlewareProps extends Record<string, any> = {},\n RouterLocals extends Record<string, any> = {}\n>(): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return new TypedRouter<RouterMiddlewareProps, RouterLocals>();\n}\n\n// Option 2: Factory with optional configuration\n\n/**\n * Configuration options for createTypedRouterWithConfig.\n *\n * @property validateInput - (Future) Whether to enable global input validation.\n * @property errorHandler - Optional global error handler middleware for the router.\n */\nexport interface RouterConfig {\n validateInput?: boolean;\n errorHandler?: (\n error: any,\n req: Request,\n res: Response,\n next: NextFunction\n ) => void;\n}\n\n/**\n * Create a new typed router with optional configuration.\n *\n * Use this if you want to add a global error handler or future global options.\n *\n * @param config - Optional configuration for the router (e.g. error handler).\n * @returns A new TypedRouter instance.\n *\n * @example\n * import { createTypedRouterWithConfig } from '@minisylar/express-typed-router';\n *\n * const router = createTypedRouterWithConfig({\n * errorHandler: (err, req, res, next) => {\n * res.status(500).json({ error: 'Something went wrong', details: err });\n * }\n * });\n */\nexport function createTypedRouterWithConfig<\n RouterMiddlewareProps extends Record<string, any> = {},\n RouterLocals extends Record<string, any> = {}\n>(config?: RouterConfig): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n const router = new TypedRouter<RouterMiddlewareProps, RouterLocals>();\n if (config?.errorHandler) {\n router.getRouter().use(config.errorHandler);\n }\n return router;\n}\n\n// Option 3: Factory with pre-configured middleware\n\n/**\n * Create a new typed router with pre-configured middleware.\n *\n * This is useful for setting up router-level middleware in a single call.\n *\n * @param middleware - One or more TypedMiddleware functions to apply to all routes.\n * @returns A new TypedRouter instance with the middleware applied.\n *\n * @example\n * import { createTypedRouterWithMiddleware } from '@minisylar/express-typed-router';\n *\n * const router = createTypedRouterWithMiddleware(authMiddleware, loggingMiddleware);\n */\nexport function createTypedRouterWithMiddleware<T extends Record<string, any>>(\n ...middleware: TypedMiddleware<any, any>[]\n): TypedRouter<T> {\n let router = new TypedRouter() as any;\n for (const mw of middleware) {\n router = router.useMiddleware(mw);\n }\n return router;\n}\n"],"mappings":"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"}
@@ -103,7 +103,7 @@ type ZodRouteHandler<Path extends string = string, BodySchema extends ZodType<an
103
103
  interface RouteOptions<BodySchema extends ZodType<any, any, any> | unknown = unknown, QuerySchema extends ZodType<any, any, any> | unknown = unknown> {
104
104
  bodySchema?: BodySchema extends ZodType<any, any, any> ? BodySchema : never;
105
105
  querySchema?: QuerySchema extends ZodType<any, any, any> ? QuerySchema : never;
106
- middleware?: readonly TypedMiddleware<any, any>[];
106
+ middleware?: TypedMiddleware<any, any>[];
107
107
  }
108
108
  type HttpMethod = "get" | "post" | "put" | "delete" | "patch" | "options" | "head" | "all";
109
109
  declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}, RouterLocals extends Record<string, any> = {}> {
@@ -112,8 +112,16 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
112
112
  /**
113
113
  * Add typed middleware that extends the request with additional properties
114
114
  * and/or adds properties to response.locals
115
- */
116
- useTypedMiddleware<TReq extends Record<string, any> = {}, TLocals extends Record<string, any> = {}>(middleware: TypedMiddleware<TReq, TLocals>): TypedRouter<RouterMiddlewareProps & TReq, RouterLocals & TLocals>;
115
+ */ /**
116
+ * Add typed middleware to the router.
117
+ * This middleware will apply to all routes defined after this call.
118
+ *
119
+ * @template TReq - Type extensions for the request object
120
+ * @template TLocals - Type extensions for response.locals
121
+ * @param middleware - The typed middleware function
122
+ * @returns A new router instance with updated types
123
+ */
124
+ useMiddleware<TReq extends Record<string, any> = {}, TLocals extends Record<string, any> = {}>(middleware: TypedMiddleware<TReq, TLocals>): TypedRouter<RouterMiddlewareProps & TReq, RouterLocals & TLocals>;
117
125
  /**
118
126
  * Get the underlying Express router
119
127
  */
@@ -123,21 +131,39 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
123
131
  get<Path extends string, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
124
132
  middleware: Middleware;
125
133
  }, handler: ZodRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<Middleware>, RouterLocals & InferMiddlewareLocals<Middleware>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
126
- get<Path extends string, BodySchema extends ZodType<any, any, any> | unknown, QuerySchema extends ZodType<any, any, any> | unknown, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: RouteOptions<BodySchema, QuerySchema> & {
127
- middleware: Middleware;
128
- }, handler: ZodRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<Middleware>, RouterLocals & InferMiddlewareLocals<Middleware>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
129
- post<Path extends string, BodySchema extends ZodType<any, any, any>, QuerySchema extends ZodType<any, any, any> | unknown, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
134
+ get<Path extends string, BodySchema extends ZodType<any, any, any> | unknown, QuerySchema extends ZodType<any, any, any> | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: RouteOptions<BodySchema, QuerySchema> & {
135
+ middleware: [...M];
136
+ },
137
+ // Using tuple spread pattern
138
+ handler: ZodRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
139
+ // Make it readonly for type inference
140
+ // Make it readonly for type inference
141
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
142
+ post<Path extends string, BodySchema extends ZodType<any, any, any>, QuerySchema extends ZodType<any, any, any> | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
130
143
  bodySchema: BodySchema;
131
144
  querySchema?: QuerySchema;
132
- middleware: Middleware;
133
- }, handler: ZodRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<Middleware>, RouterLocals & InferMiddlewareLocals<Middleware>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
134
- post<Path extends string, BodySchema extends ZodType<any, any, any>, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
145
+ middleware: [...M];
146
+ }, handler: ZodRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
147
+ // Make it readonly for type inference
148
+ // Make it readonly for type inference
149
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
150
+ post<Path extends string, BodySchema extends ZodType<any, any, any>, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
135
151
  bodySchema: BodySchema;
136
- middleware: Middleware;
137
- }, handler: ZodRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<Middleware>, RouterLocals & InferMiddlewareLocals<Middleware>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
138
- post<Path extends string, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
139
- middleware: Middleware;
140
- }, handler: ZodRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<Middleware>, RouterLocals & InferMiddlewareLocals<Middleware>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
152
+ middleware: [...M];
153
+ },
154
+ // Using tuple spread pattern
155
+ handler: ZodRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
156
+ // Make it readonly for type inference
157
+ // Make it readonly for type inference
158
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
159
+ post<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
160
+ middleware: [...M];
161
+ },
162
+ // Using tuple spread pattern
163
+ handler: ZodRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
164
+ // Make it readonly for type inference
165
+ // Make it readonly for type inference
166
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
141
167
  post<Path extends string, BodySchema extends ZodType<any, any, any> | unknown, QuerySchema extends ZodType<any, any, any> | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: ZodRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
142
168
  post<Path extends string>(path: Path, handler: ZodRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
143
169
  put<Path extends string, BodySchema extends ZodType<any, any, any>, QuerySchema extends ZodType<any, any, any> | unknown, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
@@ -103,7 +103,7 @@ type ZodRouteHandler<Path extends string = string, BodySchema extends ZodType<an
103
103
  interface RouteOptions<BodySchema extends ZodType<any, any, any> | unknown = unknown, QuerySchema extends ZodType<any, any, any> | unknown = unknown> {
104
104
  bodySchema?: BodySchema extends ZodType<any, any, any> ? BodySchema : never;
105
105
  querySchema?: QuerySchema extends ZodType<any, any, any> ? QuerySchema : never;
106
- middleware?: readonly TypedMiddleware<any, any>[];
106
+ middleware?: TypedMiddleware<any, any>[];
107
107
  }
108
108
  type HttpMethod = "get" | "post" | "put" | "delete" | "patch" | "options" | "head" | "all";
109
109
  declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}, RouterLocals extends Record<string, any> = {}> {
@@ -112,8 +112,16 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
112
112
  /**
113
113
  * Add typed middleware that extends the request with additional properties
114
114
  * and/or adds properties to response.locals
115
- */
116
- useTypedMiddleware<TReq extends Record<string, any> = {}, TLocals extends Record<string, any> = {}>(middleware: TypedMiddleware<TReq, TLocals>): TypedRouter<RouterMiddlewareProps & TReq, RouterLocals & TLocals>;
115
+ */ /**
116
+ * Add typed middleware to the router.
117
+ * This middleware will apply to all routes defined after this call.
118
+ *
119
+ * @template TReq - Type extensions for the request object
120
+ * @template TLocals - Type extensions for response.locals
121
+ * @param middleware - The typed middleware function
122
+ * @returns A new router instance with updated types
123
+ */
124
+ useMiddleware<TReq extends Record<string, any> = {}, TLocals extends Record<string, any> = {}>(middleware: TypedMiddleware<TReq, TLocals>): TypedRouter<RouterMiddlewareProps & TReq, RouterLocals & TLocals>;
117
125
  /**
118
126
  * Get the underlying Express router
119
127
  */
@@ -123,21 +131,39 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
123
131
  get<Path extends string, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
124
132
  middleware: Middleware;
125
133
  }, handler: ZodRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<Middleware>, RouterLocals & InferMiddlewareLocals<Middleware>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
126
- get<Path extends string, BodySchema extends ZodType<any, any, any> | unknown, QuerySchema extends ZodType<any, any, any> | unknown, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: RouteOptions<BodySchema, QuerySchema> & {
127
- middleware: Middleware;
128
- }, handler: ZodRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<Middleware>, RouterLocals & InferMiddlewareLocals<Middleware>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
129
- post<Path extends string, BodySchema extends ZodType<any, any, any>, QuerySchema extends ZodType<any, any, any> | unknown, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
134
+ get<Path extends string, BodySchema extends ZodType<any, any, any> | unknown, QuerySchema extends ZodType<any, any, any> | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: RouteOptions<BodySchema, QuerySchema> & {
135
+ middleware: [...M];
136
+ },
137
+ // Using tuple spread pattern
138
+ handler: ZodRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
139
+ // Make it readonly for type inference
140
+ // Make it readonly for type inference
141
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
142
+ post<Path extends string, BodySchema extends ZodType<any, any, any>, QuerySchema extends ZodType<any, any, any> | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
130
143
  bodySchema: BodySchema;
131
144
  querySchema?: QuerySchema;
132
- middleware: Middleware;
133
- }, handler: ZodRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<Middleware>, RouterLocals & InferMiddlewareLocals<Middleware>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
134
- post<Path extends string, BodySchema extends ZodType<any, any, any>, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
145
+ middleware: [...M];
146
+ }, handler: ZodRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
147
+ // Make it readonly for type inference
148
+ // Make it readonly for type inference
149
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
150
+ post<Path extends string, BodySchema extends ZodType<any, any, any>, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
135
151
  bodySchema: BodySchema;
136
- middleware: Middleware;
137
- }, handler: ZodRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<Middleware>, RouterLocals & InferMiddlewareLocals<Middleware>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
138
- post<Path extends string, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
139
- middleware: Middleware;
140
- }, handler: ZodRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<Middleware>, RouterLocals & InferMiddlewareLocals<Middleware>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
152
+ middleware: [...M];
153
+ },
154
+ // Using tuple spread pattern
155
+ handler: ZodRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
156
+ // Make it readonly for type inference
157
+ // Make it readonly for type inference
158
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
159
+ post<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
160
+ middleware: [...M];
161
+ },
162
+ // Using tuple spread pattern
163
+ handler: ZodRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
164
+ // Make it readonly for type inference
165
+ // Make it readonly for type inference
166
+ RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
141
167
  post<Path extends string, BodySchema extends ZodType<any, any, any> | unknown, QuerySchema extends ZodType<any, any, any> | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: ZodRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
142
168
  post<Path extends string>(path: Path, handler: ZodRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
143
169
  put<Path extends string, BodySchema extends ZodType<any, any, any>, QuerySchema extends ZodType<any, any, any> | unknown, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
@@ -11,7 +11,16 @@ var TypedRouter = class {
11
11
  * Add typed middleware that extends the request with additional properties
12
12
  * and/or adds properties to response.locals
13
13
  */
14
- useTypedMiddleware(middleware) {
14
+ /**
15
+ * Add typed middleware to the router.
16
+ * This middleware will apply to all routes defined after this call.
17
+ *
18
+ * @template TReq - Type extensions for the request object
19
+ * @template TLocals - Type extensions for response.locals
20
+ * @param middleware - The typed middleware function
21
+ * @returns A new router instance with updated types
22
+ */
23
+ useMiddleware(middleware) {
15
24
  this.router.use(middleware);
16
25
  return this;
17
26
  }
@@ -146,7 +155,7 @@ function createTypedRouterWithConfig(config) {
146
155
  */
147
156
  function createTypedRouterWithMiddleware(...middleware) {
148
157
  let router = new TypedRouter();
149
- for (const mw of middleware) router = router.useTypedMiddleware(mw);
158
+ for (const mw of middleware) router = router.useMiddleware(mw);
150
159
  return router;
151
160
  }
152
161
 
@@ -1 +1 @@
1
- {"version":3,"file":"zod-router.js","names":["middleware: TypedMiddleware<TReq, TLocals>","path: string","optionsOrHandler: any","handler?: any","method: HttpMethod","middlewares: any[]","schema: ZodType<any, any, any>","req: Request","res: Response","next: NextFunction","config?: RouterConfig"],"sources":["../src/zod-router.ts"],"sourcesContent":["/**\n * @packageDocumentation\n * @module @minisylar/express-typed-router\n *\n * @title @minisylar/express-typed-router\n *\n * A strongly-typed Express router with Zod validation and automatic type inference for params, body, query, and middleware.\n *\n * @example\n * // Example 1: Basic usage with router-level middleware\n * const router = createTypedRouter()\n * .useTypedMiddleware(timestampMiddleware)\n * .useTypedMiddleware(requestIdMiddleware)\n *\n * router.get('/posts/:postId', (req, res) => {\n * const { postId } = req.params // Typed as { postId: string }\n * const { timestamp, requestId } = req // Both properties are now typed correctly!\n * res.json({ postId, timestamp, requestId })\n * })\n *\n * @example\n * // Example 2: Per-route middleware with automatic type inference\n * router.post(\n * '/posts',\n * {\n * bodySchema: CreatePostSchema,\n * middleware: [timestampMiddleware, requestIdMiddleware] as const\n * },\n * (req, res) => {\n * const { title, content, tags } = req.body // From schema validation\n * const { timestamp, requestId } = req // From middleware - should be automatically typed!\n * res.json({ title, content, tags, timestamp, requestId })\n * }\n * )\n *\n * @example\n * // Example 3: Mixed middleware\n * const router = createTypedRouter().useTypedMiddleware(requestIdMiddleware)\n * router.get(\n * '/posts/:postId',\n * {\n * middleware: [authMiddleware] as const\n * },\n * (req, res) => {\n * const { postId } = req.params\n * const { requestId } = req // From router-level middleware\n * const { userId, hasPermission } = req // From per-route middleware\n * res.json({ postId, requestId, userId, hasPermission })\n * }\n * )\n *\n * @example\n * // Example 4: Using factory with pre-configured middleware\n * const router = createTypedRouterWithMiddleware(timestampMiddleware, requestIdMiddleware)\n * router.get('/simple/:id', (req, res) => {\n * const { id } = req.params\n * const { timestamp, requestId } = req // Already available from factory setup!\n * res.json({ id, timestamp, requestId })\n * })\n *\n * @example\n * // Example 5: Demonstrating all HTTP methods\n * const router = createTypedRouter().useTypedMiddleware(requestIdMiddleware)\n *\n * // GET with query validation\n * router.get('/posts', { querySchema: QuerySchema }, (req, res) => {\n * const { limit, offset } = req.query // Typed from schema\n * const { requestId } = req // From router middleware\n * res.json({ posts: [], limit, offset, requestId })\n * })\n *\n * // POST with body validation and middleware\n * router.post(\n * '/posts',\n * {\n * bodySchema: CreatePostSchema,\n * middleware: [timestampMiddleware] as const\n * },\n * (req, res) => {\n * const { title, content } = req.body // From body schema\n * const { requestId, timestamp } = req // From middleware\n * res.json({ id: 'new-post', title, content, requestId, timestamp })\n * }\n * )\n *\n * // PUT for full updates\n * router.put(\n * '/posts/:postId',\n * {\n * bodySchema: CreatePostSchema,\n * middleware: [authMiddleware] as const\n * },\n * (req, res) => {\n * const { postId } = req.params\n * const { title, content } = req.body\n * const { requestId, userId, hasPermission } = req\n * res.json({ postId, title, content, requestId, userId, hasPermission })\n * }\n * )\n *\n * // PATCH for partial updates\n * router.patch('/posts/:postId', { bodySchema: UpdatePostSchema }, (req, res) => {\n * const { postId } = req.params\n * const updates = req.body // Partial update object\n * const { requestId } = req\n * res.json({ postId, updates, requestId })\n * })\n *\n * // DELETE\n * router.delete('/posts/:postId', (req, res) => {\n * const { postId } = req.params\n * const { requestId } = req\n * res.json({ deleted: postId, requestId })\n * })\n *\n * // OPTIONS for CORS preflight\n * router.options('/posts/*', (req, res) => {\n * res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE')\n * res.header('Access-Control-Allow-Headers', 'Content-Type')\n * res.status(200).end()\n * })\n *\n * // HEAD for metadata only\n * router.head('/posts/:postId', (req, res) => {\n * const { postId } = req.params\n * res.header('X-Post-ID', postId)\n * res.status(200).end()\n * })\n *\n * // ALL method for catch-all routes\n * router.all('/debug/*', (req, res) => {\n * const { requestId } = req\n * res.json({\n * method: req.method,\n * path: req.path,\n * requestId\n * })\n * })\n */\n/* eslint-disable @typescript-eslint/no-empty-object-type */\nimport express, {\n type Request,\n type Response,\n type NextFunction,\n} from \"express\";\nimport { z, type ZodType } from \"zod\";\n\n/**\n * Extract route parameters from Express.js route patterns.\n *\n * Supports all Express.js routing patterns:\n * - Named parameters: /users/:userId → { userId: string }\n * - Multiple parameters: /users/:userId/books/:bookId → { userId: string; bookId: string }\n * - Parameters with separators: /flights/:from-:to → { from: string; to: string }\n * - Dot notation: /plantae/:genus.:species → { genus: string; species: string }\n * - Regex constraints: /user/:id(\\\\d+) → { id: string }\n * - Optional parameters: /posts/:year/:month? → { year: string; month?: string }\n * - Wildcard parameters: /files/* → { \"0\": string }\n * - Multiple wildcards: /a/star/b/star → { \"0\": string; \"1\": string }\n */\nexport type ExtractRouteParams<Path extends string> = string extends Path\n ? Record<string, string>\n : ExtractParams<Path>;\n\n/**\n * Main parameter extraction logic - enhanced for Express 5 support\n */\ntype ExtractParams<Path extends string> =\n // Handle Express 5 braces for optional segments: {/:param} or {/path/:param}\n Path extends `${infer Before}{${infer OptionalContent}}${infer After}`\n ? ExtractOptionalSegment<OptionalContent> &\n ExtractParams<`${Before}${After}`>\n : // Handle named parameters :paramName\n Path extends `${infer _Before}:${infer Rest}`\n ? ExtractSingleParam<Rest> & ExtractParams<RemoveFirstParam<Path>>\n : // Handle wildcards *\n Path extends `${infer _Before}*${infer After}`\n ? { [K in CountWildcards<_Before>]: string } & ExtractParams<After>\n : // No more parameters\n {};\n\n/**\n * Extract parameters from Express 5 optional segments in braces\n * Handles patterns like {/:param}, {.:ext}, {/optional/:param}\n */\ntype ExtractOptionalSegment<Content extends string> =\n // Handle optional parameter patterns like /:param\n Content extends `/:${infer Rest}`\n ? ExtractOptionalParam<Rest>\n : Content extends `.:${infer Rest}`\n ? ExtractOptionalParam<Rest>\n : Content extends `${infer _Path}:${infer Rest}`\n ? ExtractOptionalParam<Rest>\n : {};\n\n/**\n * Extract a single optional parameter from brace content\n */\ntype ExtractOptionalParam<Rest extends string> =\n Rest extends `${infer ParamName}/${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}-${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}.${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}`\n ? { [K in ParamName]?: string }\n : {};\n\n/**\n * Extract a single parameter name from the rest of the path\n * Enhanced to handle Express 5 patterns and optional parameters correctly\n * Special handling for consecutive parameters like :from-:to\n * Order matters: regex constraints must be handled before repeating parameters\n */\ntype ExtractSingleParam<Rest extends string> =\n // Handle regex constraints FIRST (before +, *, ?) to avoid conflicts\n Rest extends `${infer ParamName}(${infer _Constraint})${infer _After}`\n ? { [K in ParamName]: string } // Handle consecutive parameters with separators first: param-:nextParam\n : Rest extends `${infer ParamName}-:${infer _NextParam}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}.:${infer _NextParam}`\n ? { [K in ParamName]: string }\n : // Handle optional parameters followed by delimiters (before regular delimiters)\n Rest extends `${infer ParamName}?/${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?-${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?.${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?#${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?:${infer _After}`\n ? { [K in ParamName]?: string }\n : // Then handle regular delimiters (after optional parameter patterns)\n Rest extends `${infer ParamName}/${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}-${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}.${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}#${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}:${infer _After}`\n ? { [K in ParamName]: string }\n : // Handle Express 5 repeating parameters (after regular delimiters)\n Rest extends `${infer ParamName}+${infer _After}`\n ? { [K in ParamName]: string[] }\n : Rest extends `${infer ParamName}*${infer _After}`\n ? { [K in ParamName]?: string[] }\n : // Handle optional parameters with ? (Express 4) - only at the end of a segment\n Rest extends `${infer ParamName}?${infer _After}`\n ? { [K in ParamName]?: string } // Parameter at absolute end of string\n : Rest extends string\n ? Rest extends \"\"\n ? {}\n : Rest extends `${infer ParamName}?`\n ? { [K in ParamName]?: string } // ParamName here doesn't include the ?\n : Rest extends `${infer ParamName}+`\n ? { [K in ParamName]: string[] }\n : Rest extends `${infer ParamName}*`\n ? { [K in ParamName]?: string[] }\n : { [K in Rest]: string }\n : {};\n\n/**\n * Remove the first parameter from path to continue parsing\n * Enhanced to handle Express 5 patterns and optional parameters\n * Handles patterns like :from-:to by removing just :from and keeping -:to\n * Order matters: regex constraints must be handled before repeating parameters\n */\ntype RemoveFirstParam<Path extends string> =\n Path extends `${infer Before}:${infer Rest}`\n ? // Handle regex constraints FIRST (before +, *, ?) to avoid conflicts\n Rest extends `${infer _ParamName}(${infer _Constraint})${infer After}`\n ? `${Before}${After}` // Handle consecutive parameters: :param-:nextParam -> -:nextParam\n : Rest extends `${infer _ParamName}-:${infer After}`\n ? `${Before}-:${After}`\n : Rest extends `${infer _ParamName}.:${infer After}`\n ? `${Before}.:${After}`\n : // Handle optional parameters followed by delimiters (before regular delimiters)\n Rest extends `${infer _ParamName}?/${infer After}`\n ? `${Before}/${After}`\n : Rest extends `${infer _ParamName}?-${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}?.${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}?#${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}?:${infer After}`\n ? `${Before}:${After}`\n : // Handle regular separators (after optional parameter patterns)\n Rest extends `${infer _ParamName}/${infer After}`\n ? `${Before}/${After}`\n : Rest extends `${infer _ParamName}-${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}.${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}#${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}:${infer After}`\n ? `${Before}:${After}`\n : // Handle Express 5 repeating parameters (after regular separators)\n Rest extends `${infer _ParamName}+${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}*${infer After}`\n ? `${Before}${After}`\n : // Handle optional parameters with ?\n Rest extends `${infer _ParamName}?${infer After}`\n ? `${Before}${After}`\n : Before\n : Path;\n\n/**\n * Count wildcards to assign proper numeric indices\n */\ntype CountWildcards<\n Path extends string,\n Count extends string = \"0\"\n> = Path extends `${infer _Before}*${infer Rest}`\n ? CountWildcards<Rest, IncrementWildcard<Count>>\n : Count;\n\n/**\n * Helper type to increment wildcard count as string\n */\ntype IncrementWildcard<T extends string> = T extends \"0\"\n ? \"1\"\n : T extends \"1\"\n ? \"2\"\n : T extends \"2\"\n ? \"3\"\n : T extends \"3\"\n ? \"4\"\n : T extends \"4\"\n ? \"5\"\n : T extends \"5\"\n ? \"6\"\n : T extends \"6\"\n ? \"7\"\n : T extends \"7\"\n ? \"8\"\n : T extends \"8\"\n ? \"9\"\n : \"10\"; // Reasonable limit for wildcards\n\n/**\n * Express middleware that adds custom properties to the request object and/or response locals.\n *\n * @template TReq - The shape of the properties added to the request object.\n * @template TLocals - The shape of the properties added to response.locals.\n * @param req - The Express request object, extended with TReq.\n * @param res - The Express response object with typed locals.\n * @param next - The next middleware function.\n */\nexport type TypedMiddleware<\n TReq extends Record<string, any> = {},\n TLocals extends Record<string, any> = {}\n> = (\n req: Request & TReq,\n res: Response<any, TLocals>,\n next: NextFunction\n) => void | Promise<void>;\n\n/**\n * Simplified TypedMiddleware for request-only extensions (backward compatibility)\n */\nexport type RequestOnlyMiddleware<TReq extends Record<string, any>> =\n TypedMiddleware<TReq, {}>;\n\n/**\n * Simplified TypedMiddleware for response locals-only extensions\n */\nexport type LocalsOnlyMiddleware<TLocals extends Record<string, any>> =\n TypedMiddleware<{}, TLocals>;\n\n// Utility type to infer props from middleware array\ntype InferMiddlewareProps<T extends readonly TypedMiddleware<any, any>[]> =\n T extends readonly [infer First, ...infer Rest]\n ? First extends TypedMiddleware<infer FirstReq, any>\n ? Rest extends readonly TypedMiddleware<any, any>[]\n ? FirstReq & InferMiddlewareProps<Rest>\n : FirstReq\n : {}\n : {};\n\n// Utility type to infer locals from middleware array\ntype InferMiddlewareLocals<T extends readonly TypedMiddleware<any, any>[]> =\n T extends readonly [infer First, ...infer Rest]\n ? First extends TypedMiddleware<any, infer FirstLocals>\n ? Rest extends readonly TypedMiddleware<any, any>[]\n ? FirstLocals & InferMiddlewareLocals<Rest>\n : FirstLocals\n : {}\n : {};\n\n// Enhanced Request type with proper inference\nexport type ZodRequest<\n Path extends string = string,\n BodySchema extends ZodType<any, any, any> | unknown = unknown,\n QuerySchema extends ZodType<any, any, any> | unknown = unknown,\n MiddlewareProps extends Record<string, any> = {}\n> = Omit<Request, \"params\" | \"query\" | \"body\"> & {\n params: ExtractRouteParams<Path>;\n body: BodySchema extends ZodType<any, any, any>\n ? z.infer<BodySchema>\n : unknown;\n query: QuerySchema extends ZodType<any, any, any>\n ? z.infer<QuerySchema>\n : unknown;\n} & MiddlewareProps;\n\n// Route handler type\nexport type ZodRouteHandler<\n Path extends string = string,\n BodySchema extends ZodType<any, any, any> | unknown = unknown,\n QuerySchema extends ZodType<any, any, any> | unknown = unknown,\n MiddlewareProps extends Record<string, any> = {},\n ResponseLocals extends Record<string, any> = {}\n> = (\n req: ZodRequest<Path, BodySchema, QuerySchema, MiddlewareProps>,\n res: Response<any, ResponseLocals>,\n next?: NextFunction\n) => void | Promise<void> | Response | Promise<Response>;\n\n/**\n * Options for defining a typed route, including schemas and middleware.\n *\n * @template BodySchema - Zod schema for request body validation.\n * @template QuerySchema - Zod schema for query parameter validation.\n * @property bodySchema - Optional Zod schema for validating the request body.\n * @property querySchema - Optional Zod schema for validating the query string.\n * @property middleware - Optional array of TypedMiddleware for this route.\n */\nexport interface RouteOptions<\n BodySchema extends ZodType<any, any, any> | unknown = unknown,\n QuerySchema extends ZodType<any, any, any> | unknown = unknown\n> {\n bodySchema?: BodySchema extends ZodType<any, any, any> ? BodySchema : never;\n querySchema?: QuerySchema extends ZodType<any, any, any>\n ? QuerySchema\n : never;\n middleware?: readonly TypedMiddleware<any, any>[];\n}\n\n// HTTP methods\nexport type HttpMethod =\n | \"get\"\n | \"post\"\n | \"put\"\n | \"delete\"\n | \"patch\"\n | \"options\"\n | \"head\"\n | \"all\";\n\n// Main typed router class\nclass TypedRouter<\n RouterMiddlewareProps extends Record<string, any> = {},\n RouterLocals extends Record<string, any> = {}\n> {\n private router: express.Router;\n\n constructor() {\n this.router = express.Router();\n }\n /**\n * Add typed middleware that extends the request with additional properties\n * and/or adds properties to response.locals\n */\n useTypedMiddleware<\n TReq extends Record<string, any> = {},\n TLocals extends Record<string, any> = {}\n >(\n middleware: TypedMiddleware<TReq, TLocals>\n ): TypedRouter<RouterMiddlewareProps & TReq, RouterLocals & TLocals> {\n this.router.use(middleware as any);\n return this as any;\n }\n /**\n * Get the underlying Express router\n */\n getRouter(): express.Router {\n return this.router;\n }\n // Method overloads for GET requests with automatic middleware type inference\n get<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n get<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Special overload for middleware type inference\n get<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Combined overload for body/query schema + middleware\n get<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema> & { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n // Implementation\n get(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"get\", path, optionsOrHandler, handler);\n }\n // Combined overload for body/query schema + middleware (most specific first)\n post<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body schema only + middleware\n post<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n post<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body + Query schema without middleware\n post<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Just handler, no options\n post<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n post(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"post\", path, optionsOrHandler, handler);\n }\n // PUT method with all the same overloads as POST\n put<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n put(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"put\", path, optionsOrHandler, handler);\n }\n // PATCH method with all the same overloads as POST\n patch<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n patch(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"patch\", path, optionsOrHandler, handler);\n } // DELETE method (typically no body, but can have query params and middleware)\n // Most specific first: query schema + middleware\n delete<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema only\n delete<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: { querySchema: QuerySchema },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n delete<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n delete<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n delete(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"delete\", path, optionsOrHandler, handler);\n } // OPTIONS method (typically no body, used for CORS preflight)\n // Most specific first: query schema + middleware\n options<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema only\n options<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: { querySchema: QuerySchema },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n options<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n options<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n options(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"options\", path, optionsOrHandler, handler);\n } // HEAD method (like GET but only returns headers)\n // Most specific first: query schema + middleware\n head<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema only\n head<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: { querySchema: QuerySchema },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n head<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n head<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n head(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"head\", path, optionsOrHandler, handler);\n } // ALL method (matches all HTTP methods)\n // Most specific first: body + query + middleware\n all<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body schema + middleware (no query)\n all<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema + middleware (no body)\n all<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body + query schemas (no middleware)\n all<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only (no schemas)\n all<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n all<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n all(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"all\", path, optionsOrHandler, handler);\n }\n // Helper method to register routes\n private registerRoute(\n method: HttpMethod,\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n const middlewares: any[] = [];\n\n if (typeof optionsOrHandler === \"object\") {\n const options = optionsOrHandler as RouteOptions<any, any>;\n\n // Add per-route middleware first\n if (options.middleware) {\n middlewares.push(...options.middleware);\n }\n\n // Add schema validation middleware\n if (options.bodySchema) {\n middlewares.push(\n this.createBodyValidationMiddleware(options.bodySchema)\n );\n }\n if (options.querySchema) {\n middlewares.push(\n this.createQueryValidationMiddleware(options.querySchema)\n );\n }\n\n // Add the main handler\n middlewares.push(handler);\n } else {\n // Direct handler without options\n middlewares.push(optionsOrHandler);\n }\n\n // Register with Express router\n (this.router as any)[method](path, ...middlewares);\n\n return this;\n }\n\n private createBodyValidationMiddleware(schema: ZodType<any, any, any>) {\n return (req: Request, res: Response, next: NextFunction) => {\n try {\n req.body = schema.parse(req.body);\n next();\n } catch (error) {\n if (error instanceof z.ZodError) {\n res.status(400).json({\n error: \"Validation failed\",\n details: error.errors,\n });\n } else {\n next(error);\n }\n }\n };\n }\n\n private createQueryValidationMiddleware(schema: ZodType<any, any, any>) {\n return (req: Request, res: Response, next: NextFunction) => {\n try {\n req.query = schema.parse(req.query);\n next();\n } catch (error) {\n if (error instanceof z.ZodError) {\n res.status(400).json({\n error: \"Validation failed\",\n details: error.errors,\n });\n } else {\n next(error);\n }\n }\n };\n }\n}\n\n/**\n * Create a new strongly-typed Express router instance.\n *\n * This is the simplest way to get started with @minisylar/express-typed-router.\n *\n * @example\n * import { createTypedRouter } from '@minisylar/express-typed-router';\n *\n * // Create a router and add a typed GET route\n * const router = createTypedRouter();\n * router.get('/hello/:name', (req, res) => {\n * // req.params.name is typed as string\n * res.json({ message: `Hello, ${req.params.name}!` });\n * });\n *\n * // Use with Express\n * import express from 'express';\n * const app = express();\n * app.use('/api', router.getRouter());\n */\nexport function createTypedRouter<\n RouterMiddlewareProps extends Record<string, any> = {},\n RouterLocals extends Record<string, any> = {}\n>(): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return new TypedRouter<RouterMiddlewareProps, RouterLocals>();\n}\n\n// Option 2: Factory with optional configuration\n\n/**\n * Configuration options for createTypedRouterWithConfig.\n *\n * @property validateInput - (Future) Whether to enable global input validation.\n * @property errorHandler - Optional global error handler middleware for the router.\n */\nexport interface RouterConfig {\n validateInput?: boolean;\n errorHandler?: (\n error: any,\n req: Request,\n res: Response,\n next: NextFunction\n ) => void;\n}\n\n/**\n * Create a new typed router with optional configuration.\n *\n * Use this if you want to add a global error handler or future global options.\n *\n * @param config - Optional configuration for the router (e.g. error handler).\n * @returns A new TypedRouter instance.\n *\n * @example\n * import { createTypedRouterWithConfig } from '@minisylar/express-typed-router';\n *\n * const router = createTypedRouterWithConfig({\n * errorHandler: (err, req, res, next) => {\n * res.status(500).json({ error: 'Something went wrong', details: err });\n * }\n * });\n */\nexport function createTypedRouterWithConfig<\n RouterMiddlewareProps extends Record<string, any> = {},\n RouterLocals extends Record<string, any> = {}\n>(config?: RouterConfig): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n const router = new TypedRouter<RouterMiddlewareProps, RouterLocals>();\n if (config?.errorHandler) {\n router.getRouter().use(config.errorHandler);\n }\n return router;\n}\n\n// Option 3: Factory with pre-configured middleware\n\n/**\n * Create a new typed router with pre-configured middleware.\n *\n * This is useful for setting up router-level middleware in a single call.\n *\n * @param middleware - One or more TypedMiddleware functions to apply to all routes.\n * @returns A new TypedRouter instance with the middleware applied.\n *\n * @example\n * import { createTypedRouterWithMiddleware } from '@minisylar/express-typed-router';\n *\n * const router = createTypedRouterWithMiddleware(authMiddleware, loggingMiddleware);\n */\nexport function createTypedRouterWithMiddleware<T extends Record<string, any>>(\n ...middleware: TypedMiddleware<any, any>[]\n): TypedRouter<T> {\n let router = new TypedRouter() as any;\n for (const mw of middleware) {\n router = router.useTypedMiddleware(mw);\n }\n return router;\n}\n"],"mappings":";;;;AAycA,IAAM,cAAN,MAGE;CACA,AAAQ;CAER,cAAc;AACZ,OAAK,SAAS,QAAQ,QAAQ;CAC/B;;;;;CAKD,mBAIEA,YACmE;AACnE,OAAK,OAAO,IAAI,WAAkB;AAClC,SAAO;CACR;;;;CAID,YAA4B;AAC1B,SAAO,KAAK;CACb;CA+DD,IACEC,MACAC,kBACAC,SACkD;AAClD,SAAO,KAAK,cAAc,OAAO,MAAM,kBAAkB,QAAQ;CAClE;CAqFD,KACEF,MACAC,kBACAC,SACkD;AAClD,SAAO,KAAK,cAAc,QAAQ,MAAM,kBAAkB,QAAQ;CACnE;CAgFD,IACEF,MACAC,kBACAC,SACkD;AAClD,SAAO,KAAK,cAAc,OAAO,MAAM,kBAAkB,QAAQ;CAClE;CAgFD,MACEF,MACAC,kBACAC,SACkD;AAClD,SAAO,KAAK,cAAc,SAAS,MAAM,kBAAkB,QAAQ;CACpE;CA6DD,OACEF,MACAC,kBACAC,SACkD;AAClD,SAAO,KAAK,cAAc,UAAU,MAAM,kBAAkB,QAAQ;CACrE;CA6DD,QACEF,MACAC,kBACAC,SACkD;AAClD,SAAO,KAAK,cAAc,WAAW,MAAM,kBAAkB,QAAQ;CACtE;CA6DD,KACEF,MACAC,kBACAC,SACkD;AAClD,SAAO,KAAK,cAAc,QAAQ,MAAM,kBAAkB,QAAQ;CACnE;CAqGD,IACEF,MACAC,kBACAC,SACkD;AAClD,SAAO,KAAK,cAAc,OAAO,MAAM,kBAAkB,QAAQ;CAClE;CAED,AAAQ,cACNC,QACAH,MACAC,kBACAC,SACkD;EAClD,MAAME,cAAqB,CAAE;AAE7B,aAAW,qBAAqB,UAAU;GACxC,MAAM,UAAU;AAGhB,OAAI,QAAQ,WACV,aAAY,KAAK,GAAG,QAAQ,WAAW;AAIzC,OAAI,QAAQ,WACV,aAAY,KACV,KAAK,+BAA+B,QAAQ,WAAW,CACxD;AAEH,OAAI,QAAQ,YACV,aAAY,KACV,KAAK,gCAAgC,QAAQ,YAAY,CAC1D;AAIH,eAAY,KAAK,QAAQ;EAC1B,MAEC,aAAY,KAAK,iBAAiB;AAIpC,EAAC,KAAK,OAAe,QAAQ,MAAM,GAAG,YAAY;AAElD,SAAO;CACR;CAED,AAAQ,+BAA+BC,QAAgC;AACrE,SAAO,CAACC,KAAcC,KAAeC,SAAuB;AAC1D,OAAI;AACF,QAAI,OAAO,OAAO,MAAM,IAAI,KAAK;AACjC,UAAM;GACP,SAAQ,OAAO;AACd,QAAI,iBAAiB,EAAE,SACrB,KAAI,OAAO,IAAI,CAAC,KAAK;KACnB,OAAO;KACP,SAAS,MAAM;IAChB,EAAC;QAEF,MAAK,MAAM;GAEd;EACF;CACF;CAED,AAAQ,gCAAgCH,QAAgC;AACtE,SAAO,CAACC,KAAcC,KAAeC,SAAuB;AAC1D,OAAI;AACF,QAAI,QAAQ,OAAO,MAAM,IAAI,MAAM;AACnC,UAAM;GACP,SAAQ,OAAO;AACd,QAAI,iBAAiB,EAAE,SACrB,KAAI,OAAO,IAAI,CAAC,KAAK;KACnB,OAAO;KACP,SAAS,MAAM;IAChB,EAAC;QAEF,MAAK,MAAM;GAEd;EACF;CACF;AACF;;;;;;;;;;;;;;;;;;;;;AAsBD,SAAgB,oBAGsC;AACpD,QAAO,IAAI;AACZ;;;;;;;;;;;;;;;;;;AAqCD,SAAgB,4BAGdC,QAAyE;CACzE,MAAM,SAAS,IAAI;AACnB,KAAI,QAAQ,aACV,QAAO,WAAW,CAAC,IAAI,OAAO,aAAa;AAE7C,QAAO;AACR;;;;;;;;;;;;;;AAiBD,SAAgB,gCACd,GAAG,YACa;CAChB,IAAI,SAAS,IAAI;AACjB,MAAK,MAAM,MAAM,WACf,UAAS,OAAO,mBAAmB,GAAG;AAExC,QAAO;AACR"}
1
+ {"version":3,"file":"zod-router.js","names":["middleware: TypedMiddleware<TReq, TLocals>","path: string","optionsOrHandler: any","handler?: any","method: HttpMethod","middlewares: any[]","schema: ZodType<any, any, any>","req: Request","res: Response","next: NextFunction","config?: RouterConfig"],"sources":["../src/zod-router.ts"],"sourcesContent":["/**\n * @packageDocumentation\n * @module @minisylar/express-typed-router\n *\n * @title @minisylar/express-typed-router\n *\n * A strongly-typed Express router with Zod validation and automatic type inference for params, body, query, and middleware.\n *\n * @example\n * // Example 1: Basic usage with router-level middleware\n * const router = createTypedRouter()\n * .useMiddleware(timestampMiddleware)\n * .useMiddleware(requestIdMiddleware)\n *\n * router.get('/posts/:postId', (req, res) => {\n * const { postId } = req.params // Typed as { postId: string }\n * const { timestamp, requestId } = req // Both properties are now typed correctly!\n * res.json({ postId, timestamp, requestId })\n * })\n *\n * @example\n * // Example 2: Per-route middleware with automatic type inference\n * router.post(\n * '/posts',\n * {\n * bodySchema: CreatePostSchema,\n * middleware: [timestampMiddleware, requestIdMiddleware] as const\n * },\n * (req, res) => {\n * const { title, content, tags } = req.body // From schema validation\n * const { timestamp, requestId } = req // From middleware - should be automatically typed!\n * res.json({ title, content, tags, timestamp, requestId })\n * }\n * )\n *\n * @example\n * // Example 3: Mixed middleware\n * const router = createTypedRouter().useMiddleware(requestIdMiddleware)\n * router.get(\n * '/posts/:postId',\n * {\n * middleware: [authMiddleware] as const\n * },\n * (req, res) => {\n * const { postId } = req.params\n * const { requestId } = req // From router-level middleware\n * const { userId, hasPermission } = req // From per-route middleware\n * res.json({ postId, requestId, userId, hasPermission })\n * }\n * )\n *\n * @example\n * // Example 4: Using factory with pre-configured middleware\n * const router = createTypedRouterWithMiddleware(timestampMiddleware, requestIdMiddleware)\n * router.get('/simple/:id', (req, res) => {\n * const { id } = req.params\n * const { timestamp, requestId } = req // Already available from factory setup!\n * res.json({ id, timestamp, requestId })\n * })\n *\n * @example\n * // Example 5: Demonstrating all HTTP methods\n * const router = createTypedRouter().useMiddleware(requestIdMiddleware)\n *\n * // GET with query validation\n * router.get('/posts', { querySchema: QuerySchema }, (req, res) => {\n * const { limit, offset } = req.query // Typed from schema\n * const { requestId } = req // From router middleware\n * res.json({ posts: [], limit, offset, requestId })\n * })\n *\n * // POST with body validation and middleware\n * router.post(\n * '/posts',\n * {\n * bodySchema: CreatePostSchema,\n * middleware: [timestampMiddleware] as const\n * },\n * (req, res) => {\n * const { title, content } = req.body // From body schema\n * const { requestId, timestamp } = req // From middleware\n * res.json({ id: 'new-post', title, content, requestId, timestamp })\n * }\n * )\n *\n * // PUT for full updates\n * router.put(\n * '/posts/:postId',\n * {\n * bodySchema: CreatePostSchema,\n * middleware: [authMiddleware] as const\n * },\n * (req, res) => {\n * const { postId } = req.params\n * const { title, content } = req.body\n * const { requestId, userId, hasPermission } = req\n * res.json({ postId, title, content, requestId, userId, hasPermission })\n * }\n * )\n *\n * // PATCH for partial updates\n * router.patch('/posts/:postId', { bodySchema: UpdatePostSchema }, (req, res) => {\n * const { postId } = req.params\n * const updates = req.body // Partial update object\n * const { requestId } = req\n * res.json({ postId, updates, requestId })\n * })\n *\n * // DELETE\n * router.delete('/posts/:postId', (req, res) => {\n * const { postId } = req.params\n * const { requestId } = req\n * res.json({ deleted: postId, requestId })\n * })\n *\n * // OPTIONS for CORS preflight\n * router.options('/posts/*', (req, res) => {\n * res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE')\n * res.header('Access-Control-Allow-Headers', 'Content-Type')\n * res.status(200).end()\n * })\n *\n * // HEAD for metadata only\n * router.head('/posts/:postId', (req, res) => {\n * const { postId } = req.params\n * res.header('X-Post-ID', postId)\n * res.status(200).end()\n * })\n *\n * // ALL method for catch-all routes\n * router.all('/debug/*', (req, res) => {\n * const { requestId } = req\n * res.json({\n * method: req.method,\n * path: req.path,\n * requestId\n * })\n * })\n */\n/* eslint-disable @typescript-eslint/no-empty-object-type */\nimport express, {\n type Request,\n type Response,\n type NextFunction,\n} from \"express\";\nimport { z, type ZodType } from \"zod\";\n\n/**\n * Extract route parameters from Express.js route patterns.\n *\n * Supports all Express.js routing patterns:\n * - Named parameters: /users/:userId → { userId: string }\n * - Multiple parameters: /users/:userId/books/:bookId → { userId: string; bookId: string }\n * - Parameters with separators: /flights/:from-:to → { from: string; to: string }\n * - Dot notation: /plantae/:genus.:species → { genus: string; species: string }\n * - Regex constraints: /user/:id(\\\\d+) → { id: string }\n * - Optional parameters: /posts/:year/:month? → { year: string; month?: string }\n * - Wildcard parameters: /files/* → { \"0\": string }\n * - Multiple wildcards: /a/star/b/star → { \"0\": string; \"1\": string }\n */\nexport type ExtractRouteParams<Path extends string> = string extends Path\n ? Record<string, string>\n : ExtractParams<Path>;\n\n/**\n * Main parameter extraction logic - enhanced for Express 5 support\n */\ntype ExtractParams<Path extends string> =\n // Handle Express 5 braces for optional segments: {/:param} or {/path/:param}\n Path extends `${infer Before}{${infer OptionalContent}}${infer After}`\n ? ExtractOptionalSegment<OptionalContent> &\n ExtractParams<`${Before}${After}`>\n : // Handle named parameters :paramName\n Path extends `${infer _Before}:${infer Rest}`\n ? ExtractSingleParam<Rest> & ExtractParams<RemoveFirstParam<Path>>\n : // Handle wildcards *\n Path extends `${infer _Before}*${infer After}`\n ? { [K in CountWildcards<_Before>]: string } & ExtractParams<After>\n : // No more parameters\n {};\n\n/**\n * Extract parameters from Express 5 optional segments in braces\n * Handles patterns like {/:param}, {.:ext}, {/optional/:param}\n */\ntype ExtractOptionalSegment<Content extends string> =\n // Handle optional parameter patterns like /:param\n Content extends `/:${infer Rest}`\n ? ExtractOptionalParam<Rest>\n : Content extends `.:${infer Rest}`\n ? ExtractOptionalParam<Rest>\n : Content extends `${infer _Path}:${infer Rest}`\n ? ExtractOptionalParam<Rest>\n : {};\n\n/**\n * Extract a single optional parameter from brace content\n */\ntype ExtractOptionalParam<Rest extends string> =\n Rest extends `${infer ParamName}/${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}-${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}.${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}`\n ? { [K in ParamName]?: string }\n : {};\n\n/**\n * Extract a single parameter name from the rest of the path\n * Enhanced to handle Express 5 patterns and optional parameters correctly\n * Special handling for consecutive parameters like :from-:to\n * Order matters: regex constraints must be handled before repeating parameters\n */\ntype ExtractSingleParam<Rest extends string> =\n // Handle regex constraints FIRST (before +, *, ?) to avoid conflicts\n Rest extends `${infer ParamName}(${infer _Constraint})${infer _After}`\n ? { [K in ParamName]: string } // Handle consecutive parameters with separators first: param-:nextParam\n : Rest extends `${infer ParamName}-:${infer _NextParam}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}.:${infer _NextParam}`\n ? { [K in ParamName]: string }\n : // Handle optional parameters followed by delimiters (before regular delimiters)\n Rest extends `${infer ParamName}?/${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?-${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?.${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?#${infer _After}`\n ? { [K in ParamName]?: string }\n : Rest extends `${infer ParamName}?:${infer _After}`\n ? { [K in ParamName]?: string }\n : // Then handle regular delimiters (after optional parameter patterns)\n Rest extends `${infer ParamName}/${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}-${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}.${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}#${infer _After}`\n ? { [K in ParamName]: string }\n : Rest extends `${infer ParamName}:${infer _After}`\n ? { [K in ParamName]: string }\n : // Handle Express 5 repeating parameters (after regular delimiters)\n Rest extends `${infer ParamName}+${infer _After}`\n ? { [K in ParamName]: string[] }\n : Rest extends `${infer ParamName}*${infer _After}`\n ? { [K in ParamName]?: string[] }\n : // Handle optional parameters with ? (Express 4) - only at the end of a segment\n Rest extends `${infer ParamName}?${infer _After}`\n ? { [K in ParamName]?: string } // Parameter at absolute end of string\n : Rest extends string\n ? Rest extends \"\"\n ? {}\n : Rest extends `${infer ParamName}?`\n ? { [K in ParamName]?: string } // ParamName here doesn't include the ?\n : Rest extends `${infer ParamName}+`\n ? { [K in ParamName]: string[] }\n : Rest extends `${infer ParamName}*`\n ? { [K in ParamName]?: string[] }\n : { [K in Rest]: string }\n : {};\n\n/**\n * Remove the first parameter from path to continue parsing\n * Enhanced to handle Express 5 patterns and optional parameters\n * Handles patterns like :from-:to by removing just :from and keeping -:to\n * Order matters: regex constraints must be handled before repeating parameters\n */\ntype RemoveFirstParam<Path extends string> =\n Path extends `${infer Before}:${infer Rest}`\n ? // Handle regex constraints FIRST (before +, *, ?) to avoid conflicts\n Rest extends `${infer _ParamName}(${infer _Constraint})${infer After}`\n ? `${Before}${After}` // Handle consecutive parameters: :param-:nextParam -> -:nextParam\n : Rest extends `${infer _ParamName}-:${infer After}`\n ? `${Before}-:${After}`\n : Rest extends `${infer _ParamName}.:${infer After}`\n ? `${Before}.:${After}`\n : // Handle optional parameters followed by delimiters (before regular delimiters)\n Rest extends `${infer _ParamName}?/${infer After}`\n ? `${Before}/${After}`\n : Rest extends `${infer _ParamName}?-${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}?.${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}?#${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}?:${infer After}`\n ? `${Before}:${After}`\n : // Handle regular separators (after optional parameter patterns)\n Rest extends `${infer _ParamName}/${infer After}`\n ? `${Before}/${After}`\n : Rest extends `${infer _ParamName}-${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}.${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}#${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}:${infer After}`\n ? `${Before}:${After}`\n : // Handle Express 5 repeating parameters (after regular separators)\n Rest extends `${infer _ParamName}+${infer After}`\n ? `${Before}${After}`\n : Rest extends `${infer _ParamName}*${infer After}`\n ? `${Before}${After}`\n : // Handle optional parameters with ?\n Rest extends `${infer _ParamName}?${infer After}`\n ? `${Before}${After}`\n : Before\n : Path;\n\n/**\n * Count wildcards to assign proper numeric indices\n */\ntype CountWildcards<\n Path extends string,\n Count extends string = \"0\"\n> = Path extends `${infer _Before}*${infer Rest}`\n ? CountWildcards<Rest, IncrementWildcard<Count>>\n : Count;\n\n/**\n * Helper type to increment wildcard count as string\n */\ntype IncrementWildcard<T extends string> = T extends \"0\"\n ? \"1\"\n : T extends \"1\"\n ? \"2\"\n : T extends \"2\"\n ? \"3\"\n : T extends \"3\"\n ? \"4\"\n : T extends \"4\"\n ? \"5\"\n : T extends \"5\"\n ? \"6\"\n : T extends \"6\"\n ? \"7\"\n : T extends \"7\"\n ? \"8\"\n : T extends \"8\"\n ? \"9\"\n : \"10\"; // Reasonable limit for wildcards\n\n/**\n * Express middleware that adds custom properties to the request object and/or response locals.\n *\n * @template TReq - The shape of the properties added to the request object.\n * @template TLocals - The shape of the properties added to response.locals.\n * @param req - The Express request object, extended with TReq.\n * @param res - The Express response object with typed locals.\n * @param next - The next middleware function.\n */\nexport type TypedMiddleware<\n TReq extends Record<string, any> = {},\n TLocals extends Record<string, any> = {}\n> = (\n req: Request & TReq,\n res: Response<any, TLocals>,\n next: NextFunction\n) => void | Promise<void>;\n\n/**\n * Simplified TypedMiddleware for request-only extensions (backward compatibility)\n */\nexport type RequestOnlyMiddleware<TReq extends Record<string, any>> =\n TypedMiddleware<TReq, {}>;\n\n/**\n * Simplified TypedMiddleware for response locals-only extensions\n */\nexport type LocalsOnlyMiddleware<TLocals extends Record<string, any>> =\n TypedMiddleware<{}, TLocals>;\n\n// Utility type to infer props from middleware array\ntype InferMiddlewareProps<T extends readonly TypedMiddleware<any, any>[]> =\n T extends readonly [infer First, ...infer Rest]\n ? First extends TypedMiddleware<infer FirstReq, any>\n ? Rest extends readonly TypedMiddleware<any, any>[]\n ? FirstReq & InferMiddlewareProps<Rest>\n : FirstReq\n : {}\n : {};\n\n// Utility type to infer locals from middleware array\ntype InferMiddlewareLocals<T extends readonly TypedMiddleware<any, any>[]> =\n T extends readonly [infer First, ...infer Rest]\n ? First extends TypedMiddleware<any, infer FirstLocals>\n ? Rest extends readonly TypedMiddleware<any, any>[]\n ? FirstLocals & InferMiddlewareLocals<Rest>\n : FirstLocals\n : {}\n : {};\n\n// Enhanced Request type with proper inference\nexport type ZodRequest<\n Path extends string = string,\n BodySchema extends ZodType<any, any, any> | unknown = unknown,\n QuerySchema extends ZodType<any, any, any> | unknown = unknown,\n MiddlewareProps extends Record<string, any> = {}\n> = Omit<Request, \"params\" | \"query\" | \"body\"> & {\n params: ExtractRouteParams<Path>;\n body: BodySchema extends ZodType<any, any, any>\n ? z.infer<BodySchema>\n : unknown;\n query: QuerySchema extends ZodType<any, any, any>\n ? z.infer<QuerySchema>\n : unknown;\n} & MiddlewareProps;\n\n// Route handler type\nexport type ZodRouteHandler<\n Path extends string = string,\n BodySchema extends ZodType<any, any, any> | unknown = unknown,\n QuerySchema extends ZodType<any, any, any> | unknown = unknown,\n MiddlewareProps extends Record<string, any> = {},\n ResponseLocals extends Record<string, any> = {}\n> = (\n req: ZodRequest<Path, BodySchema, QuerySchema, MiddlewareProps>,\n res: Response<any, ResponseLocals>,\n next?: NextFunction\n) => void | Promise<void> | Response | Promise<Response>;\n\n/**\n * Options for defining a typed route, including schemas and middleware.\n *\n * @template BodySchema - Zod schema for request body validation.\n * @template QuerySchema - Zod schema for query parameter validation.\n * @property bodySchema - Optional Zod schema for validating the request body.\n * @property querySchema - Optional Zod schema for validating the query string.\n * @property middleware - Optional array of TypedMiddleware for this route.\n */\nexport interface RouteOptions<\n BodySchema extends ZodType<any, any, any> | unknown = unknown,\n QuerySchema extends ZodType<any, any, any> | unknown = unknown\n> {\n bodySchema?: BodySchema extends ZodType<any, any, any> ? BodySchema : never;\n querySchema?: QuerySchema extends ZodType<any, any, any>\n ? QuerySchema\n : never;\n middleware?: TypedMiddleware<any, any>[];\n}\n\n// HTTP methods\nexport type HttpMethod =\n | \"get\"\n | \"post\"\n | \"put\"\n | \"delete\"\n | \"patch\"\n | \"options\"\n | \"head\"\n | \"all\";\n\n// Main typed router class\nclass TypedRouter<\n RouterMiddlewareProps extends Record<string, any> = {},\n RouterLocals extends Record<string, any> = {}\n> {\n private router: express.Router;\n\n constructor() {\n this.router = express.Router();\n }\n /**\n * Add typed middleware that extends the request with additional properties\n * and/or adds properties to response.locals\n */ /**\n * Add typed middleware to the router.\n * This middleware will apply to all routes defined after this call.\n *\n * @template TReq - Type extensions for the request object\n * @template TLocals - Type extensions for response.locals\n * @param middleware - The typed middleware function\n * @returns A new router instance with updated types\n */\n useMiddleware<\n TReq extends Record<string, any> = {},\n TLocals extends Record<string, any> = {}\n >(\n middleware: TypedMiddleware<TReq, TLocals>\n ): TypedRouter<RouterMiddlewareProps & TReq, RouterLocals & TLocals> {\n this.router.use(middleware as any);\n return this as any;\n }\n /**\n * Get the underlying Express router\n */\n getRouter(): express.Router {\n return this.router;\n }\n // Method overloads for GET requests with automatic middleware type inference\n get<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n get<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Special overload for middleware type inference\n get<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n // Combined overload for body/query schema + middleware\n get<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown,\n M extends TypedMiddleware<any, any>[] // Using array type for JS compatibility\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema> & { middleware: [...M] }, // Using tuple spread pattern\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference\n RouterLocals & InferMiddlewareLocals<readonly [...M]>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n // Implementation\n get(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"get\", path, optionsOrHandler, handler);\n } // Combined overload for body/query schema + middleware (most specific first)\n post<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n M extends TypedMiddleware<any, any>[] // Using array type for JS compatibility\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: [...M]; // Using tuple spread pattern\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference\n RouterLocals & InferMiddlewareLocals<readonly [...M]>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body schema only + middleware\n post<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n M extends TypedMiddleware<any, any>[] // Using array type for JS compatibility\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: [...M] }, // Using tuple spread pattern\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference\n RouterLocals & InferMiddlewareLocals<readonly [...M]>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n post<\n Path extends string,\n M extends TypedMiddleware<any, any>[] // Using array type for JS compatibility\n >(\n path: Path,\n options: { middleware: [...M] }, // Using tuple spread pattern\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>, // Make it readonly for type inference\n RouterLocals & InferMiddlewareLocals<readonly [...M]>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body + Query schema without middleware\n post<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Just handler, no options\n post<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n post(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"post\", path, optionsOrHandler, handler);\n }\n // PUT method with all the same overloads as POST\n put<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n put<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n put(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"put\", path, optionsOrHandler, handler);\n }\n // PATCH method with all the same overloads as POST\n patch<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n patch<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n patch(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"patch\", path, optionsOrHandler, handler);\n } // DELETE method (typically no body, but can have query params and middleware)\n // Most specific first: query schema + middleware\n delete<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema only\n delete<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: { querySchema: QuerySchema },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n delete<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n delete<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n delete(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"delete\", path, optionsOrHandler, handler);\n } // OPTIONS method (typically no body, used for CORS preflight)\n // Most specific first: query schema + middleware\n options<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema only\n options<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: { querySchema: QuerySchema },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n options<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n options<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n options(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"options\", path, optionsOrHandler, handler);\n } // HEAD method (like GET but only returns headers)\n // Most specific first: query schema + middleware\n head<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema only\n head<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: { querySchema: QuerySchema },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only\n head<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n head<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n head(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"head\", path, optionsOrHandler, handler);\n } // ALL method (matches all HTTP methods)\n // Most specific first: body + query + middleware\n all<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: {\n bodySchema: BodySchema;\n querySchema?: QuerySchema;\n middleware: Middleware;\n },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body schema + middleware (no query)\n all<\n Path extends string,\n BodySchema extends ZodType<any, any, any>,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { bodySchema: BodySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Query schema + middleware (no body)\n all<\n Path extends string,\n QuerySchema extends ZodType<any, any, any> | unknown,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { querySchema: QuerySchema; middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n QuerySchema,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Body + query schemas (no middleware)\n all<\n Path extends string,\n BodySchema extends ZodType<any, any, any> | unknown,\n QuerySchema extends ZodType<any, any, any> | unknown\n >(\n path: Path,\n options: RouteOptions<BodySchema, QuerySchema>,\n handler: ZodRouteHandler<\n Path,\n BodySchema,\n QuerySchema,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Middleware only (no schemas)\n all<\n Path extends string,\n Middleware extends readonly TypedMiddleware<any, any>[]\n >(\n path: Path,\n options: { middleware: Middleware },\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps & InferMiddlewareProps<Middleware>,\n RouterLocals & InferMiddlewareLocals<Middleware>\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n\n // Basic overload with no options\n all<Path extends string>(\n path: Path,\n handler: ZodRouteHandler<\n Path,\n unknown,\n unknown,\n RouterMiddlewareProps,\n RouterLocals\n >\n ): TypedRouter<RouterMiddlewareProps, RouterLocals>;\n all(\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return this.registerRoute(\"all\", path, optionsOrHandler, handler);\n }\n // Helper method to register routes\n private registerRoute(\n method: HttpMethod,\n path: string,\n optionsOrHandler: any,\n handler?: any\n ): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n const middlewares: any[] = [];\n\n if (typeof optionsOrHandler === \"object\") {\n const options = optionsOrHandler as RouteOptions<any, any>;\n\n // Add per-route middleware first\n if (options.middleware) {\n middlewares.push(...options.middleware);\n }\n\n // Add schema validation middleware\n if (options.bodySchema) {\n middlewares.push(\n this.createBodyValidationMiddleware(options.bodySchema)\n );\n }\n if (options.querySchema) {\n middlewares.push(\n this.createQueryValidationMiddleware(options.querySchema)\n );\n }\n\n // Add the main handler\n middlewares.push(handler);\n } else {\n // Direct handler without options\n middlewares.push(optionsOrHandler);\n }\n\n // Register with Express router\n (this.router as any)[method](path, ...middlewares);\n\n return this;\n }\n\n private createBodyValidationMiddleware(schema: ZodType<any, any, any>) {\n return (req: Request, res: Response, next: NextFunction) => {\n try {\n req.body = schema.parse(req.body);\n next();\n } catch (error) {\n if (error instanceof z.ZodError) {\n res.status(400).json({\n error: \"Validation failed\",\n details: error.errors,\n });\n } else {\n next(error);\n }\n }\n };\n }\n\n private createQueryValidationMiddleware(schema: ZodType<any, any, any>) {\n return (req: Request, res: Response, next: NextFunction) => {\n try {\n req.query = schema.parse(req.query);\n next();\n } catch (error) {\n if (error instanceof z.ZodError) {\n res.status(400).json({\n error: \"Validation failed\",\n details: error.errors,\n });\n } else {\n next(error);\n }\n }\n };\n }\n}\n\n/**\n * Create a new strongly-typed Express router instance.\n *\n * This is the simplest way to get started with @minisylar/express-typed-router.\n *\n * @example\n * import { createTypedRouter } from '@minisylar/express-typed-router';\n *\n * // Create a router and add a typed GET route\n * const router = createTypedRouter();\n * router.get('/hello/:name', (req, res) => {\n * // req.params.name is typed as string\n * res.json({ message: `Hello, ${req.params.name}!` });\n * });\n *\n * // Use with Express\n * import express from 'express';\n * const app = express();\n * app.use('/api', router.getRouter());\n */\nexport function createTypedRouter<\n RouterMiddlewareProps extends Record<string, any> = {},\n RouterLocals extends Record<string, any> = {}\n>(): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n return new TypedRouter<RouterMiddlewareProps, RouterLocals>();\n}\n\n// Option 2: Factory with optional configuration\n\n/**\n * Configuration options for createTypedRouterWithConfig.\n *\n * @property validateInput - (Future) Whether to enable global input validation.\n * @property errorHandler - Optional global error handler middleware for the router.\n */\nexport interface RouterConfig {\n validateInput?: boolean;\n errorHandler?: (\n error: any,\n req: Request,\n res: Response,\n next: NextFunction\n ) => void;\n}\n\n/**\n * Create a new typed router with optional configuration.\n *\n * Use this if you want to add a global error handler or future global options.\n *\n * @param config - Optional configuration for the router (e.g. error handler).\n * @returns A new TypedRouter instance.\n *\n * @example\n * import { createTypedRouterWithConfig } from '@minisylar/express-typed-router';\n *\n * const router = createTypedRouterWithConfig({\n * errorHandler: (err, req, res, next) => {\n * res.status(500).json({ error: 'Something went wrong', details: err });\n * }\n * });\n */\nexport function createTypedRouterWithConfig<\n RouterMiddlewareProps extends Record<string, any> = {},\n RouterLocals extends Record<string, any> = {}\n>(config?: RouterConfig): TypedRouter<RouterMiddlewareProps, RouterLocals> {\n const router = new TypedRouter<RouterMiddlewareProps, RouterLocals>();\n if (config?.errorHandler) {\n router.getRouter().use(config.errorHandler);\n }\n return router;\n}\n\n// Option 3: Factory with pre-configured middleware\n\n/**\n * Create a new typed router with pre-configured middleware.\n *\n * This is useful for setting up router-level middleware in a single call.\n *\n * @param middleware - One or more TypedMiddleware functions to apply to all routes.\n * @returns A new TypedRouter instance with the middleware applied.\n *\n * @example\n * import { createTypedRouterWithMiddleware } from '@minisylar/express-typed-router';\n *\n * const router = createTypedRouterWithMiddleware(authMiddleware, loggingMiddleware);\n */\nexport function createTypedRouterWithMiddleware<T extends Record<string, any>>(\n ...middleware: TypedMiddleware<any, any>[]\n): TypedRouter<T> {\n let router = new TypedRouter() as any;\n for (const mw of middleware) {\n router = router.useMiddleware(mw);\n }\n return router;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minisylar/express-typed-router",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A strongly-typed Express router with Zod validation and automatic type inference for params, body, query, and middleware",
5
5
  "type": "module",
6
6
  "main": "dist/zod-router.cjs",