@minisylar/express-typed-router 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -17
- package/dist/schema-router.cjs +1 -0
- package/dist/{zod-router.d.cts → schema-router.d.cts} +96 -84
- package/dist/{zod-router.d.ts → schema-router.d.ts} +96 -84
- package/dist/schema-router.js +1 -0
- package/package.json +25 -13
- package/dist/zod-router.cjs +0 -1
- package/dist/zod-router.js +0 -1
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# @minisylar/express-typed-router
|
|
2
2
|
|
|
3
|
-
A strongly-typed Express router with
|
|
3
|
+
A strongly-typed Express router with schema validation and automatic type inference for params, body, query, and middleware.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- 🚀 **Full TypeScript support** with automatic type inference for route parameters
|
|
8
|
-
- 🛡️ **
|
|
8
|
+
- 🛡️ **Schema validation** for request body, query parameters, and route params (Zod, Yup, Valibot, Arktype,Joi,Effect,decoders,
|
|
9
|
+
ts.data.json,
|
|
10
|
+
unhoax, etc.)
|
|
9
11
|
- 🔗 **Express.js compatibility** - works with Express 4 and Express 5
|
|
10
12
|
- 🤝 **Mix with existing Express routes** - seamlessly integrates with your current codebase
|
|
11
13
|
- 📝 **JSDoc documentation** with comprehensive examples
|
|
@@ -22,32 +24,53 @@ pnpm add @minisylar/express-typed-router
|
|
|
22
24
|
yarn add @minisylar/express-typed-router
|
|
23
25
|
```
|
|
24
26
|
|
|
25
|
-
> **Note:** This package requires Express 4.18.0+ or Express 5.0.0
|
|
27
|
+
> **Note:** This package requires Express 4.18.0+ or Express 5.0.0+. For schema validation the library works with multiple popular schema libraries (examples below).
|
|
26
28
|
|
|
27
|
-
###
|
|
29
|
+
### Schema Compatibility
|
|
28
30
|
|
|
29
|
-
This library
|
|
31
|
+
This library is schema-agnostic: it provides a validation plumbing that works with multiple popular schema libraries. Below are short examples showing how you can use different schema libraries with the router. The router expects a schema-like object that can validate input; most adapters are straightforward.
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
- ✅ **Zod v4** - Full support including Core and Mini packages
|
|
33
|
-
- 🔄 **Runtime detection** - Automatically detects and handles both versions
|
|
34
|
-
|
|
35
|
-
You can use schemas from either version or mix them in the same application:
|
|
33
|
+
Example with Zod (v3 or v4):
|
|
36
34
|
|
|
37
35
|
```typescript
|
|
38
|
-
//
|
|
39
|
-
import { z } from "zod/v4";
|
|
36
|
+
import { z } from "zod"; // or "zod/v4" or "zod/v3" as needed
|
|
40
37
|
const userSchema = z.object({ name: z.string() });
|
|
38
|
+
router.post("/users", { bodySchema: userSchema }, handler);
|
|
39
|
+
```
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
import * as z4 from "zod/v3";
|
|
44
|
-
const postSchema = z4.object({ title: z4.string() });
|
|
41
|
+
Example with Yup:
|
|
45
42
|
|
|
46
|
-
|
|
43
|
+
```javascript
|
|
44
|
+
import * as yup from "yup";
|
|
45
|
+
const userSchema = yup.object({ name: yup.string().required() });
|
|
46
|
+
// pass the yup schema directly as bodySchema; the router will run validation
|
|
47
|
+
router.post("/users", { bodySchema: userSchema }, handler);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Example with Valibot (valibot):
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { object, string } from "valibot";
|
|
54
|
+
const userSchema = object({ name: string() });
|
|
47
55
|
router.post("/users", { bodySchema: userSchema }, handler);
|
|
48
|
-
router.post("/posts", { bodySchema: postSchema }, handler);
|
|
49
56
|
```
|
|
50
57
|
|
|
58
|
+
Example with Arktype:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { object, string } from "arktype";
|
|
62
|
+
const userSchema = object({ name: string() });
|
|
63
|
+
router.post("/users", { bodySchema: userSchema }, handler);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
If a schema library needs an adapter (for example to map its errors to the router's error format), add a small wrapper that runs validation and throws the expected error shape. See the project's examples for concrete adapter patterns.
|
|
67
|
+
|
|
68
|
+
Note about Joi: Joi's TypeScript typings do not reliably infer the output type from the runtime schema shape. When using Joi you should either:
|
|
69
|
+
|
|
70
|
+
- provide an explicit generic type for the schema (e.g. `Joi.object<MyType>(...)`),
|
|
71
|
+
- add a variable type annotation (e.g. `const s: Joi.ObjectSchema<MyType> = Joi.object(...)`), or
|
|
72
|
+
- write a small adapter that validates at runtime and exposes a typed result to TypeScript.
|
|
73
|
+
|
|
51
74
|
## Quick Start
|
|
52
75
|
|
|
53
76
|
```javascript
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=s(require(`express`)),l=s(require(`@standard-schema/utils`));function u(e,t){let n=e;if(n&&n[`~standard`]&&typeof n[`~standard`].validate==`function`){let e=n[`~standard`].validate(t);if(e instanceof Promise)throw TypeError(`Async schema validation is not supported by parseSchema`);if(e.issues)throw new l.SchemaError(e.issues);return e.value}throw TypeError(`Unsupported schema shape for parseSchema`)}function d(e,t){let n=e;if(n&&n[`~standard`]&&typeof n[`~standard`].validate==`function`)return n[`~standard`].validate(t);if(n&&typeof n.safeParse==`function`)return n.safeParse(t);if(n&&typeof n.parse==`function`)try{let e=n.parse(t);return{value:e}}catch(e){return{issues:[{message:e?.message??String(e)}]}}if(n&&typeof n.validate==`function`){let e=n.validate(t);return e&&e.then&&typeof e.then==`function`?e.then(e=>e.error?{issues:[{message:e.error.message}]}:e.issues?{issues:e.issues}:{value:e.value??e}):e&&e.error?{issues:[{message:e.error.message}]}:e&&e.issues?{issues:e.issues}:{value:e.value??e}}return{issues:[{message:`Unsupported schema shape`}]}}function f(e){return typeof e==`object`&&!!e&&`issues`in e&&Array.isArray(e.issues)}var p=class{router;constructor(){this.router=c.default.Router()}useMiddleware(e){return this.router.use(e),this}getRouter(){return this.router}get(e,t,n){return this.registerRoute(`get`,e,t,n)}post(e,t,n){return this.registerRoute(`post`,e,t,n)}put(e,t,n){return this.registerRoute(`put`,e,t,n)}patch(e,t,n){return this.registerRoute(`patch`,e,t,n)}delete(e,t,n){return this.registerRoute(`delete`,e,t,n)}options(e,t,n){return this.registerRoute(`options`,e,t,n)}head(e,t,n){return this.registerRoute(`head`,e,t,n)}all(e,t,n){return this.registerRoute(`all`,e,t,n)}registerRoute(e,t,n,r){let i=[];if(typeof n==`object`){let e=n;e.middleware&&i.push(...e.middleware),e.bodySchema&&i.push(this.createBodyValidationMiddleware(e.bodySchema)),e.querySchema&&i.push(this.createQueryValidationMiddleware(e.querySchema)),i.push(r)}else i.push(n);return this.router[e](t,...i),this}createBodyValidationMiddleware(e){return async(t,n,r)=>{try{let i=d(e,t.body),a=i&&typeof i.then==`function`?await i:i;if(a&&`issues`in a&&a.issues){n.status(400).json({error:`Validation failed`,details:a.errors||a.issues});return}t.body=a&&`value`in a?a.value:a,r()}catch(e){f(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):r(e)}}}createQueryValidationMiddleware(e){return async(t,n,r)=>{try{let i=d(e,t.query),a=i&&typeof i.then==`function`?await i:i;if(a&&`issues`in a&&a.issues){n.status(400).json({error:`Validation failed`,details:a.errors||a.issues});return}let o=a&&`value`in a?a.value:a;Object.defineProperty(t,`query`,{value:o,writable:!1,enumerable:!0,configurable:!0}),r()}catch(e){f(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):r(e)}}}};function m(){return new p}function h(e){let t=new p;return e?.errorHandler&&t.getRouter().use(e.errorHandler),t}function g(...e){let t=new p;for(let n of e)t=t.useMiddleware(n);return t}exports.createTypedRouter=m,exports.createTypedRouterWithConfig=h,exports.createTypedRouterWithMiddleware=g,exports.isSchemaError=f,exports.parseSchema=u,exports.safeParseSchema=d;
|
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
import express, { NextFunction, Request, Response } from "express";
|
|
2
|
-
import
|
|
3
|
-
import * as z4 from "zod/v4/core";
|
|
2
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
4
3
|
|
|
5
|
-
//#region src/
|
|
4
|
+
//#region src/schema-router.d.ts
|
|
6
5
|
|
|
7
|
-
type
|
|
8
|
-
|
|
9
|
-
type
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
} ?
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
declare function
|
|
6
|
+
type AnyStandardSchema = StandardSchemaV1<any, any>;
|
|
7
|
+
type InferOutput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<T> : unknown;
|
|
8
|
+
type InferInput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferInput<T> : unknown;
|
|
9
|
+
type InferFromSafeParse<T> = T extends {
|
|
10
|
+
safeParse: (...args: any[]) => infer R;
|
|
11
|
+
} ? R extends {
|
|
12
|
+
success: true;
|
|
13
|
+
data: infer O;
|
|
14
|
+
} ? O : R extends Promise<infer PR> ? PR extends {
|
|
15
|
+
success: true;
|
|
16
|
+
data: infer O;
|
|
17
|
+
} ? O : never : never : T extends {
|
|
18
|
+
parse: (...args: any[]) => infer R;
|
|
19
|
+
} ? R : never;
|
|
20
|
+
type InferSchemaOutput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<T> : InferFromSafeParse<T>;
|
|
21
|
+
declare function parseSchema<T>(schema: T, data: unknown): InferSchemaOutput<T>;
|
|
22
|
+
type SafeParseResult<T> = StandardSchemaV1.Result<InferSchemaOutput<T>> | {
|
|
23
|
+
value: InferSchemaOutput<T>;
|
|
24
|
+
} | {
|
|
25
|
+
issues: any[];
|
|
26
|
+
} | Promise<StandardSchemaV1.Result<InferSchemaOutput<T>> | {
|
|
27
|
+
value: InferSchemaOutput<T>;
|
|
28
|
+
} | {
|
|
29
|
+
issues: any[];
|
|
30
|
+
}>;
|
|
31
|
+
declare function safeParseSchema<T>(schema: T, data: unknown): SafeParseResult<T>;
|
|
32
|
+
declare function isSchemaError(error: unknown): error is {
|
|
33
|
+
issues: any[];
|
|
34
|
+
};
|
|
23
35
|
/**
|
|
24
36
|
* Extract route parameters from Express.js route patterns.
|
|
25
37
|
*
|
|
@@ -28,7 +40,7 @@ declare function isZodError(error: unknown): error is z3.ZodError;
|
|
|
28
40
|
* - Multiple parameters: /users/:userId/books/:bookId → { userId: string; bookId: string }
|
|
29
41
|
* - Parameters with separators: /flights/:from-:to → { from: string; to: string }
|
|
30
42
|
* - Dot notation: /plantae/:genus.:species → { genus: string; species: string }
|
|
31
|
-
* - Regex constraints: /user/:id(
|
|
43
|
+
* - Regex constraints: /user/:id(\d+) → { id: string }
|
|
32
44
|
* - Optional parameters: /posts/:year/:month? → { year: string; month?: string }
|
|
33
45
|
* - Wildcard parameters: /files/* → { "0": string }
|
|
34
46
|
* - Multiple wildcards: /a/star/b/star → { "0": string; "1": string }
|
|
@@ -37,7 +49,7 @@ type ExtractRouteParams<Path extends string> = string extends Path ? Record<stri
|
|
|
37
49
|
/**
|
|
38
50
|
* Main parameter extraction logic - enhanced for Express 5 support with recursion depth limit
|
|
39
51
|
*/
|
|
40
|
-
type ExtractParams<Path extends string> = Path extends `${infer Before}{${infer OptionalContent}}${infer After}` ? ExtractOptionalSegment<OptionalContent> & ExtractParams<`${Before}${After}`> : Path extends `${infer _Before}:${infer Rest}` ? ExtractSingleParam<Rest> & ExtractParams<RemoveFirstParam<Path>> : Path extends `${infer _Before}*${infer After}` ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<After> : {};
|
|
52
|
+
type ExtractParams<Path extends string> = Path extends `${infer Before}{${infer OptionalContent}}${infer After}` ? ExtractOptionalSegment<OptionalContent> & ExtractParams<`${Before}${After}`> : Path extends `${infer _Before}:${infer Rest}` ? ExtractSingleParam<Rest> & ExtractParams<RemoveFirstParam<Path>> : Path extends `${infer _Before}*${infer Name}/${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`/${After}`> : { [K in Name]: string[] } & ExtractParams<`/${After}`> : Path extends `${infer _Before}*${infer Name}-${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`-${After}`> : { [K in Name]: string[] } & ExtractParams<`-${After}`> : Path extends `${infer _Before}*${infer Name}.${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`.${After}`> : { [K in Name]: string[] } & ExtractParams<`.${After}`> : Path extends `${infer _Before}*${infer Name}#${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`#${After}`> : { [K in Name]: string[] } & ExtractParams<`#${After}`> : Path extends `${infer _Before}*${infer Name}:${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`:${After}`> : { [K in Name]: string[] } & ExtractParams<`:${After}`> : Path extends `${infer _Before}*${infer Name}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<``> : { [K in Name]: string[] } & ExtractParams<``> : Path extends `${infer _Before}*${infer After}` ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<After> : {};
|
|
41
53
|
/**
|
|
42
54
|
* Extract parameters from Express 5 optional segments in braces
|
|
43
55
|
* Handles patterns like {/:param}, {.:ext}, {/optional/:param}
|
|
@@ -89,22 +101,22 @@ type RequestOnlyMiddleware<TReq extends Record<string, any>> = TypedMiddleware<T
|
|
|
89
101
|
type LocalsOnlyMiddleware<TLocals extends Record<string, any>> = TypedMiddleware<{}, TLocals>;
|
|
90
102
|
type InferMiddlewareProps<T extends readonly TypedMiddleware<any, any>[]> = T extends readonly [infer First, ...infer Rest] ? First extends TypedMiddleware<infer FirstReq, any> ? Rest extends readonly TypedMiddleware<any, any>[] ? FirstReq & InferMiddlewareProps<Rest> : FirstReq : {} : {};
|
|
91
103
|
type InferMiddlewareLocals<T extends readonly TypedMiddleware<any, any>[]> = T extends readonly [infer First, ...infer Rest] ? First extends TypedMiddleware<any, infer FirstLocals> ? Rest extends readonly TypedMiddleware<any, any>[] ? FirstLocals & InferMiddlewareLocals<Rest> : FirstLocals : {} : {};
|
|
92
|
-
type
|
|
104
|
+
type SchemaRequest<Path extends string = string, BodySchema extends AnyStandardSchema | unknown = unknown, QuerySchema extends AnyStandardSchema | unknown = unknown, MiddlewareProps extends Record<string, any> = {}> = Omit<Request, "params" | "query" | "body"> & {
|
|
93
105
|
params: ExtractRouteParams<Path>;
|
|
94
|
-
body: BodySchema extends
|
|
95
|
-
query: QuerySchema extends
|
|
106
|
+
body: BodySchema extends unknown ? InferSchemaOutput<BodySchema> : unknown;
|
|
107
|
+
query: QuerySchema extends unknown ? InferSchemaOutput<QuerySchema> : unknown;
|
|
96
108
|
} & MiddlewareProps;
|
|
97
|
-
type
|
|
109
|
+
type SchemaRouteHandler<Path extends string = string, BodySchema extends AnyStandardSchema | unknown = unknown, QuerySchema extends AnyStandardSchema | unknown = unknown, MiddlewareProps extends Record<string, any> = {}, ResponseLocals extends Record<string, any> = {}> = (req: SchemaRequest<Path, BodySchema, QuerySchema, MiddlewareProps>, res: Response<any, ResponseLocals>, next?: NextFunction) => void | undefined | Promise<void | undefined> | Response | Promise<Response> | Promise<Response | undefined>;
|
|
98
110
|
/**
|
|
99
111
|
* Options for defining a typed route, including schemas and middleware.
|
|
100
112
|
*
|
|
101
|
-
* @template BodySchema -
|
|
102
|
-
* @template QuerySchema -
|
|
103
|
-
* @property bodySchema - Optional
|
|
104
|
-
* @property querySchema - Optional
|
|
113
|
+
* @template BodySchema - Schema for request body validation.
|
|
114
|
+
* @template QuerySchema - Schema for query parameter validation.
|
|
115
|
+
* @property bodySchema - Optional schema for validating the request body.
|
|
116
|
+
* @property querySchema - Optional schema for validating the query string.
|
|
105
117
|
* @property middleware - Optional array of TypedMiddleware for this route.
|
|
106
118
|
*/
|
|
107
|
-
interface RouteOptions<BodySchema extends
|
|
119
|
+
interface RouteOptions<BodySchema extends AnyStandardSchema | unknown = unknown, QuerySchema extends AnyStandardSchema | unknown = unknown> {
|
|
108
120
|
bodySchema?: BodySchema;
|
|
109
121
|
querySchema?: QuerySchema;
|
|
110
122
|
middleware?: TypedMiddleware<any, any>[];
|
|
@@ -130,33 +142,33 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
|
|
|
130
142
|
* Get the underlying Express router
|
|
131
143
|
*/
|
|
132
144
|
getRouter(): express.Router;
|
|
133
|
-
get<Path extends string>(path: Path, handler:
|
|
134
|
-
get<Path extends string, BodySchema extends
|
|
145
|
+
get<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
146
|
+
get<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
135
147
|
get<Path extends string, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
136
148
|
middleware: Middleware;
|
|
137
|
-
}, handler:
|
|
138
|
-
get<Path extends string, BodySchema extends
|
|
149
|
+
}, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<Middleware>, RouterLocals & InferMiddlewareLocals<Middleware>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
150
|
+
get<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: RouteOptions<BodySchema, QuerySchema> & {
|
|
139
151
|
middleware: [...M];
|
|
140
152
|
},
|
|
141
153
|
// Using tuple spread pattern
|
|
142
|
-
handler:
|
|
154
|
+
handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
143
155
|
// Make it readonly for type inference
|
|
144
156
|
// Make it readonly for type inference
|
|
145
157
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
146
|
-
post<Path extends string, BodySchema extends
|
|
158
|
+
post<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
147
159
|
bodySchema: BodySchema;
|
|
148
160
|
querySchema?: QuerySchema;
|
|
149
161
|
middleware: [...M];
|
|
150
|
-
}, handler:
|
|
162
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
151
163
|
// Make it readonly for type inference
|
|
152
164
|
// Make it readonly for type inference
|
|
153
165
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
154
|
-
post<Path extends string, BodySchema extends
|
|
166
|
+
post<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
155
167
|
bodySchema: BodySchema;
|
|
156
168
|
middleware: [...M];
|
|
157
169
|
},
|
|
158
170
|
// Using tuple spread pattern
|
|
159
|
-
handler:
|
|
171
|
+
handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
160
172
|
// Make it readonly for type inference
|
|
161
173
|
// Make it readonly for type inference
|
|
162
174
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
@@ -164,26 +176,26 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
|
|
|
164
176
|
middleware: [...M];
|
|
165
177
|
},
|
|
166
178
|
// Using tuple spread pattern
|
|
167
|
-
handler:
|
|
179
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
168
180
|
// Make it readonly for type inference
|
|
169
181
|
// Make it readonly for type inference
|
|
170
182
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
171
|
-
post<Path extends string, BodySchema extends
|
|
172
|
-
post<Path extends string>(path: Path, handler:
|
|
173
|
-
put<Path extends string, BodySchema extends
|
|
183
|
+
post<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
184
|
+
post<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
185
|
+
put<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
174
186
|
bodySchema: BodySchema;
|
|
175
187
|
querySchema?: QuerySchema;
|
|
176
188
|
middleware: [...M];
|
|
177
|
-
}, handler:
|
|
189
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
178
190
|
// Make it readonly for type inference
|
|
179
191
|
// Make it readonly for type inference
|
|
180
192
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
181
|
-
put<Path extends string, BodySchema extends
|
|
193
|
+
put<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
182
194
|
bodySchema: BodySchema;
|
|
183
195
|
middleware: [...M];
|
|
184
196
|
},
|
|
185
197
|
// Using tuple spread pattern
|
|
186
|
-
handler:
|
|
198
|
+
handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
187
199
|
// Make it readonly for type inference
|
|
188
200
|
// Make it readonly for type inference
|
|
189
201
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
@@ -191,26 +203,26 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
|
|
|
191
203
|
middleware: [...M];
|
|
192
204
|
},
|
|
193
205
|
// Using tuple spread pattern
|
|
194
|
-
handler:
|
|
206
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
195
207
|
// Make it readonly for type inference
|
|
196
208
|
// Make it readonly for type inference
|
|
197
209
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
198
|
-
put<Path extends string, BodySchema extends
|
|
199
|
-
put<Path extends string>(path: Path, handler:
|
|
200
|
-
patch<Path extends string, BodySchema extends
|
|
210
|
+
put<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
211
|
+
put<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
212
|
+
patch<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
201
213
|
bodySchema: BodySchema;
|
|
202
214
|
querySchema?: QuerySchema;
|
|
203
215
|
middleware: [...M];
|
|
204
|
-
}, handler:
|
|
216
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
205
217
|
// Make it readonly for type inference
|
|
206
218
|
// Make it readonly for type inference
|
|
207
219
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
208
|
-
patch<Path extends string, BodySchema extends
|
|
220
|
+
patch<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
209
221
|
bodySchema: BodySchema;
|
|
210
222
|
middleware: [...M];
|
|
211
223
|
},
|
|
212
224
|
// Using tuple spread pattern
|
|
213
|
-
handler:
|
|
225
|
+
handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
214
226
|
// Make it readonly for type inference
|
|
215
227
|
// Make it readonly for type inference
|
|
216
228
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
@@ -218,111 +230,111 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
|
|
|
218
230
|
middleware: [...M];
|
|
219
231
|
},
|
|
220
232
|
// Using tuple spread pattern
|
|
221
|
-
handler:
|
|
233
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
222
234
|
// Make it readonly for type inference
|
|
223
235
|
// Make it readonly for type inference
|
|
224
236
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
225
|
-
patch<Path extends string, BodySchema extends
|
|
226
|
-
patch<Path extends string>(path: Path, handler:
|
|
227
|
-
delete<Path extends string, QuerySchema extends
|
|
237
|
+
patch<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
238
|
+
patch<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
239
|
+
delete<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
228
240
|
querySchema: QuerySchema;
|
|
229
241
|
middleware: [...M];
|
|
230
242
|
},
|
|
231
243
|
// Using tuple spread pattern
|
|
232
|
-
handler:
|
|
244
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
233
245
|
// Make it readonly for type inference
|
|
234
246
|
// Make it readonly for type inference
|
|
235
247
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
236
|
-
delete<Path extends string, QuerySchema extends
|
|
248
|
+
delete<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
237
249
|
querySchema: QuerySchema;
|
|
238
|
-
}, handler:
|
|
250
|
+
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
239
251
|
delete<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
240
252
|
middleware: [...M];
|
|
241
253
|
},
|
|
242
254
|
// Using tuple spread pattern
|
|
243
|
-
handler:
|
|
255
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
244
256
|
// Make it readonly for type inference
|
|
245
257
|
// Make it readonly for type inference
|
|
246
258
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
247
|
-
delete<Path extends string>(path: Path, handler:
|
|
248
|
-
options<Path extends string, QuerySchema extends
|
|
259
|
+
delete<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
260
|
+
options<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
249
261
|
querySchema: QuerySchema;
|
|
250
262
|
middleware: [...M];
|
|
251
263
|
},
|
|
252
264
|
// Using tuple spread pattern
|
|
253
|
-
handler:
|
|
265
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
254
266
|
// Make it readonly for type inference
|
|
255
267
|
// Make it readonly for type inference
|
|
256
268
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
257
|
-
options<Path extends string, QuerySchema extends
|
|
269
|
+
options<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
258
270
|
querySchema: QuerySchema;
|
|
259
|
-
}, handler:
|
|
271
|
+
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
260
272
|
options<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
261
273
|
middleware: [...M];
|
|
262
274
|
},
|
|
263
275
|
// Using tuple spread pattern
|
|
264
|
-
handler:
|
|
276
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
265
277
|
// Make it readonly for type inference
|
|
266
278
|
// Make it readonly for type inference
|
|
267
279
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
268
|
-
options<Path extends string>(path: Path, handler:
|
|
269
|
-
head<Path extends string, QuerySchema extends
|
|
280
|
+
options<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
281
|
+
head<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
270
282
|
querySchema: QuerySchema;
|
|
271
283
|
middleware: [...M];
|
|
272
284
|
},
|
|
273
285
|
// Using tuple spread pattern
|
|
274
|
-
handler:
|
|
286
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
275
287
|
// Make it readonly for type inference
|
|
276
288
|
// Make it readonly for type inference
|
|
277
289
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
278
|
-
head<Path extends string, QuerySchema extends
|
|
290
|
+
head<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
279
291
|
querySchema: QuerySchema;
|
|
280
|
-
}, handler:
|
|
292
|
+
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
281
293
|
head<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
282
294
|
middleware: [...M];
|
|
283
295
|
},
|
|
284
296
|
// Using tuple spread pattern
|
|
285
|
-
handler:
|
|
297
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
286
298
|
// Make it readonly for type inference
|
|
287
299
|
// Make it readonly for type inference
|
|
288
300
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
289
|
-
head<Path extends string>(path: Path, handler:
|
|
290
|
-
all<Path extends string, BodySchema extends
|
|
301
|
+
head<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
302
|
+
all<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
291
303
|
bodySchema: BodySchema;
|
|
292
304
|
querySchema?: QuerySchema;
|
|
293
305
|
middleware: [...M];
|
|
294
|
-
}, handler:
|
|
306
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
295
307
|
// Make it readonly for type inference
|
|
296
308
|
// Make it readonly for type inference
|
|
297
309
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
298
|
-
all<Path extends string, BodySchema extends
|
|
310
|
+
all<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
299
311
|
bodySchema: BodySchema;
|
|
300
312
|
middleware: [...M];
|
|
301
313
|
},
|
|
302
314
|
// Using tuple spread pattern
|
|
303
|
-
handler:
|
|
315
|
+
handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
304
316
|
// Make it readonly for type inference
|
|
305
317
|
// Make it readonly for type inference
|
|
306
318
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
307
|
-
all<Path extends string, QuerySchema extends
|
|
319
|
+
all<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
308
320
|
querySchema: QuerySchema;
|
|
309
321
|
middleware: [...M];
|
|
310
322
|
},
|
|
311
323
|
// Using tuple spread pattern
|
|
312
|
-
handler:
|
|
324
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
313
325
|
// Make it readonly for type inference
|
|
314
326
|
// Make it readonly for type inference
|
|
315
327
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
316
|
-
all<Path extends string, BodySchema extends
|
|
328
|
+
all<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
317
329
|
all<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
318
330
|
middleware: [...M];
|
|
319
331
|
},
|
|
320
332
|
// Using tuple spread pattern
|
|
321
|
-
handler:
|
|
333
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
322
334
|
// Make it readonly for type inference
|
|
323
335
|
// Make it readonly for type inference
|
|
324
336
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
325
|
-
all<Path extends string>(path: Path, handler:
|
|
337
|
+
all<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
326
338
|
private registerRoute;
|
|
327
339
|
private createBodyValidationMiddleware;
|
|
328
340
|
private createQueryValidationMiddleware;
|
|
@@ -391,4 +403,4 @@ declare function createTypedRouterWithConfig<RouterMiddlewareProps extends Recor
|
|
|
391
403
|
*/
|
|
392
404
|
declare function createTypedRouterWithMiddleware<T extends Record<string, any>>(...middleware: TypedMiddleware<any, any>[]): TypedRouter<T>;
|
|
393
405
|
//#endregion
|
|
394
|
-
export {
|
|
406
|
+
export { AnyStandardSchema, ExtractRouteParams, HttpMethod, InferInput, InferOutput, InferSchemaOutput, LocalsOnlyMiddleware, RequestOnlyMiddleware, RouteOptions, RouterConfig, SafeParseResult, SchemaRequest, SchemaRouteHandler, TypedMiddleware, createTypedRouter, createTypedRouterWithConfig, createTypedRouterWithMiddleware, isSchemaError, parseSchema, safeParseSchema };
|
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
import express, { NextFunction, Request, Response } from "express";
|
|
2
|
-
import
|
|
3
|
-
import * as z4 from "zod/v4/core";
|
|
2
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
4
3
|
|
|
5
|
-
//#region src/
|
|
4
|
+
//#region src/schema-router.d.ts
|
|
6
5
|
|
|
7
|
-
type
|
|
8
|
-
|
|
9
|
-
type
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
} ?
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
declare function
|
|
6
|
+
type AnyStandardSchema = StandardSchemaV1<any, any>;
|
|
7
|
+
type InferOutput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<T> : unknown;
|
|
8
|
+
type InferInput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferInput<T> : unknown;
|
|
9
|
+
type InferFromSafeParse<T> = T extends {
|
|
10
|
+
safeParse: (...args: any[]) => infer R;
|
|
11
|
+
} ? R extends {
|
|
12
|
+
success: true;
|
|
13
|
+
data: infer O;
|
|
14
|
+
} ? O : R extends Promise<infer PR> ? PR extends {
|
|
15
|
+
success: true;
|
|
16
|
+
data: infer O;
|
|
17
|
+
} ? O : never : never : T extends {
|
|
18
|
+
parse: (...args: any[]) => infer R;
|
|
19
|
+
} ? R : never;
|
|
20
|
+
type InferSchemaOutput<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<T> : InferFromSafeParse<T>;
|
|
21
|
+
declare function parseSchema<T>(schema: T, data: unknown): InferSchemaOutput<T>;
|
|
22
|
+
type SafeParseResult<T> = StandardSchemaV1.Result<InferSchemaOutput<T>> | {
|
|
23
|
+
value: InferSchemaOutput<T>;
|
|
24
|
+
} | {
|
|
25
|
+
issues: any[];
|
|
26
|
+
} | Promise<StandardSchemaV1.Result<InferSchemaOutput<T>> | {
|
|
27
|
+
value: InferSchemaOutput<T>;
|
|
28
|
+
} | {
|
|
29
|
+
issues: any[];
|
|
30
|
+
}>;
|
|
31
|
+
declare function safeParseSchema<T>(schema: T, data: unknown): SafeParseResult<T>;
|
|
32
|
+
declare function isSchemaError(error: unknown): error is {
|
|
33
|
+
issues: any[];
|
|
34
|
+
};
|
|
23
35
|
/**
|
|
24
36
|
* Extract route parameters from Express.js route patterns.
|
|
25
37
|
*
|
|
@@ -28,7 +40,7 @@ declare function isZodError(error: unknown): error is z3.ZodError;
|
|
|
28
40
|
* - Multiple parameters: /users/:userId/books/:bookId → { userId: string; bookId: string }
|
|
29
41
|
* - Parameters with separators: /flights/:from-:to → { from: string; to: string }
|
|
30
42
|
* - Dot notation: /plantae/:genus.:species → { genus: string; species: string }
|
|
31
|
-
* - Regex constraints: /user/:id(
|
|
43
|
+
* - Regex constraints: /user/:id(\d+) → { id: string }
|
|
32
44
|
* - Optional parameters: /posts/:year/:month? → { year: string; month?: string }
|
|
33
45
|
* - Wildcard parameters: /files/* → { "0": string }
|
|
34
46
|
* - Multiple wildcards: /a/star/b/star → { "0": string; "1": string }
|
|
@@ -37,7 +49,7 @@ type ExtractRouteParams<Path extends string> = string extends Path ? Record<stri
|
|
|
37
49
|
/**
|
|
38
50
|
* Main parameter extraction logic - enhanced for Express 5 support with recursion depth limit
|
|
39
51
|
*/
|
|
40
|
-
type ExtractParams<Path extends string> = Path extends `${infer Before}{${infer OptionalContent}}${infer After}` ? ExtractOptionalSegment<OptionalContent> & ExtractParams<`${Before}${After}`> : Path extends `${infer _Before}:${infer Rest}` ? ExtractSingleParam<Rest> & ExtractParams<RemoveFirstParam<Path>> : Path extends `${infer _Before}*${infer After}` ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<After> : {};
|
|
52
|
+
type ExtractParams<Path extends string> = Path extends `${infer Before}{${infer OptionalContent}}${infer After}` ? ExtractOptionalSegment<OptionalContent> & ExtractParams<`${Before}${After}`> : Path extends `${infer _Before}:${infer Rest}` ? ExtractSingleParam<Rest> & ExtractParams<RemoveFirstParam<Path>> : Path extends `${infer _Before}*${infer Name}/${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`/${After}`> : { [K in Name]: string[] } & ExtractParams<`/${After}`> : Path extends `${infer _Before}*${infer Name}-${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`-${After}`> : { [K in Name]: string[] } & ExtractParams<`-${After}`> : Path extends `${infer _Before}*${infer Name}.${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`.${After}`> : { [K in Name]: string[] } & ExtractParams<`.${After}`> : Path extends `${infer _Before}*${infer Name}#${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`#${After}`> : { [K in Name]: string[] } & ExtractParams<`#${After}`> : Path extends `${infer _Before}*${infer Name}:${infer After}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<`:${After}`> : { [K in Name]: string[] } & ExtractParams<`:${After}`> : Path extends `${infer _Before}*${infer Name}` ? Name extends "" ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<``> : { [K in Name]: string[] } & ExtractParams<``> : Path extends `${infer _Before}*${infer After}` ? { [K in CountWildcards<_Before, "0">]: string } & ExtractParams<After> : {};
|
|
41
53
|
/**
|
|
42
54
|
* Extract parameters from Express 5 optional segments in braces
|
|
43
55
|
* Handles patterns like {/:param}, {.:ext}, {/optional/:param}
|
|
@@ -89,22 +101,22 @@ type RequestOnlyMiddleware<TReq extends Record<string, any>> = TypedMiddleware<T
|
|
|
89
101
|
type LocalsOnlyMiddleware<TLocals extends Record<string, any>> = TypedMiddleware<{}, TLocals>;
|
|
90
102
|
type InferMiddlewareProps<T extends readonly TypedMiddleware<any, any>[]> = T extends readonly [infer First, ...infer Rest] ? First extends TypedMiddleware<infer FirstReq, any> ? Rest extends readonly TypedMiddleware<any, any>[] ? FirstReq & InferMiddlewareProps<Rest> : FirstReq : {} : {};
|
|
91
103
|
type InferMiddlewareLocals<T extends readonly TypedMiddleware<any, any>[]> = T extends readonly [infer First, ...infer Rest] ? First extends TypedMiddleware<any, infer FirstLocals> ? Rest extends readonly TypedMiddleware<any, any>[] ? FirstLocals & InferMiddlewareLocals<Rest> : FirstLocals : {} : {};
|
|
92
|
-
type
|
|
104
|
+
type SchemaRequest<Path extends string = string, BodySchema extends AnyStandardSchema | unknown = unknown, QuerySchema extends AnyStandardSchema | unknown = unknown, MiddlewareProps extends Record<string, any> = {}> = Omit<Request, "params" | "query" | "body"> & {
|
|
93
105
|
params: ExtractRouteParams<Path>;
|
|
94
|
-
body: BodySchema extends
|
|
95
|
-
query: QuerySchema extends
|
|
106
|
+
body: BodySchema extends unknown ? InferSchemaOutput<BodySchema> : unknown;
|
|
107
|
+
query: QuerySchema extends unknown ? InferSchemaOutput<QuerySchema> : unknown;
|
|
96
108
|
} & MiddlewareProps;
|
|
97
|
-
type
|
|
109
|
+
type SchemaRouteHandler<Path extends string = string, BodySchema extends AnyStandardSchema | unknown = unknown, QuerySchema extends AnyStandardSchema | unknown = unknown, MiddlewareProps extends Record<string, any> = {}, ResponseLocals extends Record<string, any> = {}> = (req: SchemaRequest<Path, BodySchema, QuerySchema, MiddlewareProps>, res: Response<any, ResponseLocals>, next?: NextFunction) => void | undefined | Promise<void | undefined> | Response | Promise<Response> | Promise<Response | undefined>;
|
|
98
110
|
/**
|
|
99
111
|
* Options for defining a typed route, including schemas and middleware.
|
|
100
112
|
*
|
|
101
|
-
* @template BodySchema -
|
|
102
|
-
* @template QuerySchema -
|
|
103
|
-
* @property bodySchema - Optional
|
|
104
|
-
* @property querySchema - Optional
|
|
113
|
+
* @template BodySchema - Schema for request body validation.
|
|
114
|
+
* @template QuerySchema - Schema for query parameter validation.
|
|
115
|
+
* @property bodySchema - Optional schema for validating the request body.
|
|
116
|
+
* @property querySchema - Optional schema for validating the query string.
|
|
105
117
|
* @property middleware - Optional array of TypedMiddleware for this route.
|
|
106
118
|
*/
|
|
107
|
-
interface RouteOptions<BodySchema extends
|
|
119
|
+
interface RouteOptions<BodySchema extends AnyStandardSchema | unknown = unknown, QuerySchema extends AnyStandardSchema | unknown = unknown> {
|
|
108
120
|
bodySchema?: BodySchema;
|
|
109
121
|
querySchema?: QuerySchema;
|
|
110
122
|
middleware?: TypedMiddleware<any, any>[];
|
|
@@ -130,33 +142,33 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
|
|
|
130
142
|
* Get the underlying Express router
|
|
131
143
|
*/
|
|
132
144
|
getRouter(): express.Router;
|
|
133
|
-
get<Path extends string>(path: Path, handler:
|
|
134
|
-
get<Path extends string, BodySchema extends
|
|
145
|
+
get<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
146
|
+
get<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
135
147
|
get<Path extends string, Middleware extends readonly TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
136
148
|
middleware: Middleware;
|
|
137
|
-
}, handler:
|
|
138
|
-
get<Path extends string, BodySchema extends
|
|
149
|
+
}, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<Middleware>, RouterLocals & InferMiddlewareLocals<Middleware>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
150
|
+
get<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: RouteOptions<BodySchema, QuerySchema> & {
|
|
139
151
|
middleware: [...M];
|
|
140
152
|
},
|
|
141
153
|
// Using tuple spread pattern
|
|
142
|
-
handler:
|
|
154
|
+
handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
143
155
|
// Make it readonly for type inference
|
|
144
156
|
// Make it readonly for type inference
|
|
145
157
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
146
|
-
post<Path extends string, BodySchema extends
|
|
158
|
+
post<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
147
159
|
bodySchema: BodySchema;
|
|
148
160
|
querySchema?: QuerySchema;
|
|
149
161
|
middleware: [...M];
|
|
150
|
-
}, handler:
|
|
162
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
151
163
|
// Make it readonly for type inference
|
|
152
164
|
// Make it readonly for type inference
|
|
153
165
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
154
|
-
post<Path extends string, BodySchema extends
|
|
166
|
+
post<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
155
167
|
bodySchema: BodySchema;
|
|
156
168
|
middleware: [...M];
|
|
157
169
|
},
|
|
158
170
|
// Using tuple spread pattern
|
|
159
|
-
handler:
|
|
171
|
+
handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
160
172
|
// Make it readonly for type inference
|
|
161
173
|
// Make it readonly for type inference
|
|
162
174
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
@@ -164,26 +176,26 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
|
|
|
164
176
|
middleware: [...M];
|
|
165
177
|
},
|
|
166
178
|
// Using tuple spread pattern
|
|
167
|
-
handler:
|
|
179
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
168
180
|
// Make it readonly for type inference
|
|
169
181
|
// Make it readonly for type inference
|
|
170
182
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
171
|
-
post<Path extends string, BodySchema extends
|
|
172
|
-
post<Path extends string>(path: Path, handler:
|
|
173
|
-
put<Path extends string, BodySchema extends
|
|
183
|
+
post<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
184
|
+
post<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
185
|
+
put<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
174
186
|
bodySchema: BodySchema;
|
|
175
187
|
querySchema?: QuerySchema;
|
|
176
188
|
middleware: [...M];
|
|
177
|
-
}, handler:
|
|
189
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
178
190
|
// Make it readonly for type inference
|
|
179
191
|
// Make it readonly for type inference
|
|
180
192
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
181
|
-
put<Path extends string, BodySchema extends
|
|
193
|
+
put<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
182
194
|
bodySchema: BodySchema;
|
|
183
195
|
middleware: [...M];
|
|
184
196
|
},
|
|
185
197
|
// Using tuple spread pattern
|
|
186
|
-
handler:
|
|
198
|
+
handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
187
199
|
// Make it readonly for type inference
|
|
188
200
|
// Make it readonly for type inference
|
|
189
201
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
@@ -191,26 +203,26 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
|
|
|
191
203
|
middleware: [...M];
|
|
192
204
|
},
|
|
193
205
|
// Using tuple spread pattern
|
|
194
|
-
handler:
|
|
206
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
195
207
|
// Make it readonly for type inference
|
|
196
208
|
// Make it readonly for type inference
|
|
197
209
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
198
|
-
put<Path extends string, BodySchema extends
|
|
199
|
-
put<Path extends string>(path: Path, handler:
|
|
200
|
-
patch<Path extends string, BodySchema extends
|
|
210
|
+
put<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
211
|
+
put<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
212
|
+
patch<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
201
213
|
bodySchema: BodySchema;
|
|
202
214
|
querySchema?: QuerySchema;
|
|
203
215
|
middleware: [...M];
|
|
204
|
-
}, handler:
|
|
216
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
205
217
|
// Make it readonly for type inference
|
|
206
218
|
// Make it readonly for type inference
|
|
207
219
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
208
|
-
patch<Path extends string, BodySchema extends
|
|
220
|
+
patch<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
209
221
|
bodySchema: BodySchema;
|
|
210
222
|
middleware: [...M];
|
|
211
223
|
},
|
|
212
224
|
// Using tuple spread pattern
|
|
213
|
-
handler:
|
|
225
|
+
handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
214
226
|
// Make it readonly for type inference
|
|
215
227
|
// Make it readonly for type inference
|
|
216
228
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
@@ -218,111 +230,111 @@ declare class TypedRouter<RouterMiddlewareProps extends Record<string, any> = {}
|
|
|
218
230
|
middleware: [...M];
|
|
219
231
|
},
|
|
220
232
|
// Using tuple spread pattern
|
|
221
|
-
handler:
|
|
233
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
222
234
|
// Make it readonly for type inference
|
|
223
235
|
// Make it readonly for type inference
|
|
224
236
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
225
|
-
patch<Path extends string, BodySchema extends
|
|
226
|
-
patch<Path extends string>(path: Path, handler:
|
|
227
|
-
delete<Path extends string, QuerySchema extends
|
|
237
|
+
patch<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
238
|
+
patch<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
239
|
+
delete<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
228
240
|
querySchema: QuerySchema;
|
|
229
241
|
middleware: [...M];
|
|
230
242
|
},
|
|
231
243
|
// Using tuple spread pattern
|
|
232
|
-
handler:
|
|
244
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
233
245
|
// Make it readonly for type inference
|
|
234
246
|
// Make it readonly for type inference
|
|
235
247
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
236
|
-
delete<Path extends string, QuerySchema extends
|
|
248
|
+
delete<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
237
249
|
querySchema: QuerySchema;
|
|
238
|
-
}, handler:
|
|
250
|
+
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
239
251
|
delete<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
240
252
|
middleware: [...M];
|
|
241
253
|
},
|
|
242
254
|
// Using tuple spread pattern
|
|
243
|
-
handler:
|
|
255
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
244
256
|
// Make it readonly for type inference
|
|
245
257
|
// Make it readonly for type inference
|
|
246
258
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
247
|
-
delete<Path extends string>(path: Path, handler:
|
|
248
|
-
options<Path extends string, QuerySchema extends
|
|
259
|
+
delete<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
260
|
+
options<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
249
261
|
querySchema: QuerySchema;
|
|
250
262
|
middleware: [...M];
|
|
251
263
|
},
|
|
252
264
|
// Using tuple spread pattern
|
|
253
|
-
handler:
|
|
265
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
254
266
|
// Make it readonly for type inference
|
|
255
267
|
// Make it readonly for type inference
|
|
256
268
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
257
|
-
options<Path extends string, QuerySchema extends
|
|
269
|
+
options<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
258
270
|
querySchema: QuerySchema;
|
|
259
|
-
}, handler:
|
|
271
|
+
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
260
272
|
options<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
261
273
|
middleware: [...M];
|
|
262
274
|
},
|
|
263
275
|
// Using tuple spread pattern
|
|
264
|
-
handler:
|
|
276
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
265
277
|
// Make it readonly for type inference
|
|
266
278
|
// Make it readonly for type inference
|
|
267
279
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
268
|
-
options<Path extends string>(path: Path, handler:
|
|
269
|
-
head<Path extends string, QuerySchema extends
|
|
280
|
+
options<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
281
|
+
head<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
270
282
|
querySchema: QuerySchema;
|
|
271
283
|
middleware: [...M];
|
|
272
284
|
},
|
|
273
285
|
// Using tuple spread pattern
|
|
274
|
-
handler:
|
|
286
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
275
287
|
// Make it readonly for type inference
|
|
276
288
|
// Make it readonly for type inference
|
|
277
289
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
278
|
-
head<Path extends string, QuerySchema extends
|
|
290
|
+
head<Path extends string, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: {
|
|
279
291
|
querySchema: QuerySchema;
|
|
280
|
-
}, handler:
|
|
292
|
+
}, handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
281
293
|
head<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
282
294
|
middleware: [...M];
|
|
283
295
|
},
|
|
284
296
|
// Using tuple spread pattern
|
|
285
|
-
handler:
|
|
297
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
286
298
|
// Make it readonly for type inference
|
|
287
299
|
// Make it readonly for type inference
|
|
288
300
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
289
|
-
head<Path extends string>(path: Path, handler:
|
|
290
|
-
all<Path extends string, BodySchema extends
|
|
301
|
+
head<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
302
|
+
all<Path extends string, BodySchema extends AnyStandardSchema, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
291
303
|
bodySchema: BodySchema;
|
|
292
304
|
querySchema?: QuerySchema;
|
|
293
305
|
middleware: [...M];
|
|
294
|
-
}, handler:
|
|
306
|
+
}, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
295
307
|
// Make it readonly for type inference
|
|
296
308
|
// Make it readonly for type inference
|
|
297
309
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
298
|
-
all<Path extends string, BodySchema extends
|
|
310
|
+
all<Path extends string, BodySchema extends AnyStandardSchema, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
299
311
|
bodySchema: BodySchema;
|
|
300
312
|
middleware: [...M];
|
|
301
313
|
},
|
|
302
314
|
// Using tuple spread pattern
|
|
303
|
-
handler:
|
|
315
|
+
handler: SchemaRouteHandler<Path, BodySchema, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
304
316
|
// Make it readonly for type inference
|
|
305
317
|
// Make it readonly for type inference
|
|
306
318
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
307
|
-
all<Path extends string, QuerySchema extends
|
|
319
|
+
all<Path extends string, QuerySchema extends AnyStandardSchema | unknown, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
308
320
|
querySchema: QuerySchema;
|
|
309
321
|
middleware: [...M];
|
|
310
322
|
},
|
|
311
323
|
// Using tuple spread pattern
|
|
312
|
-
handler:
|
|
324
|
+
handler: SchemaRouteHandler<Path, unknown, QuerySchema, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
313
325
|
// Make it readonly for type inference
|
|
314
326
|
// Make it readonly for type inference
|
|
315
327
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
316
|
-
all<Path extends string, BodySchema extends
|
|
328
|
+
all<Path extends string, BodySchema extends AnyStandardSchema | unknown, QuerySchema extends AnyStandardSchema | unknown>(path: Path, options: RouteOptions<BodySchema, QuerySchema>, handler: SchemaRouteHandler<Path, BodySchema, QuerySchema, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
317
329
|
all<Path extends string, M extends TypedMiddleware<any, any>[]>(path: Path, options: {
|
|
318
330
|
middleware: [...M];
|
|
319
331
|
},
|
|
320
332
|
// Using tuple spread pattern
|
|
321
|
-
handler:
|
|
333
|
+
handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps & InferMiddlewareProps<readonly [...M]>,
|
|
322
334
|
// Make it readonly for type inference
|
|
323
335
|
// Make it readonly for type inference
|
|
324
336
|
RouterLocals & InferMiddlewareLocals<readonly [...M]>>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
325
|
-
all<Path extends string>(path: Path, handler:
|
|
337
|
+
all<Path extends string>(path: Path, handler: SchemaRouteHandler<Path, unknown, unknown, RouterMiddlewareProps, RouterLocals>): TypedRouter<RouterMiddlewareProps, RouterLocals>;
|
|
326
338
|
private registerRoute;
|
|
327
339
|
private createBodyValidationMiddleware;
|
|
328
340
|
private createQueryValidationMiddleware;
|
|
@@ -391,4 +403,4 @@ declare function createTypedRouterWithConfig<RouterMiddlewareProps extends Recor
|
|
|
391
403
|
*/
|
|
392
404
|
declare function createTypedRouterWithMiddleware<T extends Record<string, any>>(...middleware: TypedMiddleware<any, any>[]): TypedRouter<T>;
|
|
393
405
|
//#endregion
|
|
394
|
-
export {
|
|
406
|
+
export { AnyStandardSchema, ExtractRouteParams, HttpMethod, InferInput, InferOutput, InferSchemaOutput, LocalsOnlyMiddleware, RequestOnlyMiddleware, RouteOptions, RouterConfig, SafeParseResult, SchemaRequest, SchemaRouteHandler, TypedMiddleware, createTypedRouter, createTypedRouterWithConfig, createTypedRouterWithMiddleware, isSchemaError, parseSchema, safeParseSchema };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"express";import{SchemaError as t}from"@standard-schema/utils";function n(e,n){let r=e;if(r&&r[`~standard`]&&typeof r[`~standard`].validate==`function`){let e=r[`~standard`].validate(n);if(e instanceof Promise)throw TypeError(`Async schema validation is not supported by parseSchema`);if(e.issues)throw new t(e.issues);return e.value}throw TypeError(`Unsupported schema shape for parseSchema`)}function r(e,t){let n=e;if(n&&n[`~standard`]&&typeof n[`~standard`].validate==`function`)return n[`~standard`].validate(t);if(n&&typeof n.safeParse==`function`)return n.safeParse(t);if(n&&typeof n.parse==`function`)try{let e=n.parse(t);return{value:e}}catch(e){return{issues:[{message:e?.message??String(e)}]}}if(n&&typeof n.validate==`function`){let e=n.validate(t);return e&&e.then&&typeof e.then==`function`?e.then(e=>e.error?{issues:[{message:e.error.message}]}:e.issues?{issues:e.issues}:{value:e.value??e}):e&&e.error?{issues:[{message:e.error.message}]}:e&&e.issues?{issues:e.issues}:{value:e.value??e}}return{issues:[{message:`Unsupported schema shape`}]}}function i(e){return typeof e==`object`&&!!e&&`issues`in e&&Array.isArray(e.issues)}var a=class{router;constructor(){this.router=e.Router()}useMiddleware(e){return this.router.use(e),this}getRouter(){return this.router}get(e,t,n){return this.registerRoute(`get`,e,t,n)}post(e,t,n){return this.registerRoute(`post`,e,t,n)}put(e,t,n){return this.registerRoute(`put`,e,t,n)}patch(e,t,n){return this.registerRoute(`patch`,e,t,n)}delete(e,t,n){return this.registerRoute(`delete`,e,t,n)}options(e,t,n){return this.registerRoute(`options`,e,t,n)}head(e,t,n){return this.registerRoute(`head`,e,t,n)}all(e,t,n){return this.registerRoute(`all`,e,t,n)}registerRoute(e,t,n,r){let i=[];if(typeof n==`object`){let e=n;e.middleware&&i.push(...e.middleware),e.bodySchema&&i.push(this.createBodyValidationMiddleware(e.bodySchema)),e.querySchema&&i.push(this.createQueryValidationMiddleware(e.querySchema)),i.push(r)}else i.push(n);return this.router[e](t,...i),this}createBodyValidationMiddleware(e){return async(t,n,a)=>{try{let i=r(e,t.body),o=i&&typeof i.then==`function`?await i:i;if(o&&`issues`in o&&o.issues){n.status(400).json({error:`Validation failed`,details:o.errors||o.issues});return}t.body=o&&`value`in o?o.value:o,a()}catch(e){i(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):a(e)}}}createQueryValidationMiddleware(e){return async(t,n,a)=>{try{let i=r(e,t.query),o=i&&typeof i.then==`function`?await i:i;if(o&&`issues`in o&&o.issues){n.status(400).json({error:`Validation failed`,details:o.errors||o.issues});return}let s=o&&`value`in o?o.value:o;Object.defineProperty(t,`query`,{value:s,writable:!1,enumerable:!0,configurable:!0}),a()}catch(e){i(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):a(e)}}}};function o(){return new a}function s(e){let t=new a;return e?.errorHandler&&t.getRouter().use(e.errorHandler),t}function c(...e){let t=new a;for(let n of e)t=t.useMiddleware(n);return t}export{o as createTypedRouter,s as createTypedRouterWithConfig,c as createTypedRouterWithMiddleware,i as isSchemaError,n as parseSchema,r as safeParseSchema};
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minisylar/express-typed-router",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
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
|
-
"main": "dist/
|
|
7
|
-
"module": "dist/
|
|
8
|
-
"types": "dist/
|
|
6
|
+
"main": "dist/schema-router.cjs",
|
|
7
|
+
"module": "dist/schema-router.js",
|
|
8
|
+
"types": "dist/schema-router.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
-
"import": "./dist/
|
|
12
|
-
"require": "./dist/
|
|
13
|
-
"types": "./dist/
|
|
11
|
+
"import": "./dist/schema-router.js",
|
|
12
|
+
"require": "./dist/schema-router.cjs",
|
|
13
|
+
"types": "./dist/schema-router.d.ts"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
@@ -36,10 +36,20 @@
|
|
|
36
36
|
"router",
|
|
37
37
|
"typescript",
|
|
38
38
|
"zod",
|
|
39
|
+
"yup",
|
|
40
|
+
"effect",
|
|
41
|
+
"joi",
|
|
42
|
+
"valibot",
|
|
43
|
+
"arktype",
|
|
44
|
+
"decoders",
|
|
45
|
+
"ts.data.json",
|
|
46
|
+
"unhoax",
|
|
39
47
|
"validation",
|
|
40
48
|
"middleware",
|
|
41
49
|
"type-safe",
|
|
42
|
-
"strongly-typed"
|
|
50
|
+
"strongly-typed",
|
|
51
|
+
"schema",
|
|
52
|
+
"standard schema"
|
|
43
53
|
],
|
|
44
54
|
"author": "Mini-Sylar",
|
|
45
55
|
"license": "MIT",
|
|
@@ -53,8 +63,11 @@
|
|
|
53
63
|
"homepage": "https://github.com/Mini-Sylar/express-typed-router#readme",
|
|
54
64
|
"packageManager": "pnpm@10.11.0",
|
|
55
65
|
"peerDependencies": {
|
|
56
|
-
"express": "^4.18.0 || ^5.0.0"
|
|
57
|
-
|
|
66
|
+
"express": "^4.18.0 || ^5.0.0"
|
|
67
|
+
},
|
|
68
|
+
"dependencies": {
|
|
69
|
+
"@standard-schema/spec": "^1.0.0",
|
|
70
|
+
"@standard-schema/utils": "^0.3.0"
|
|
58
71
|
},
|
|
59
72
|
"devDependencies": {
|
|
60
73
|
"@semantic-release/changelog": "^6.0.3",
|
|
@@ -63,9 +76,8 @@
|
|
|
63
76
|
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
64
77
|
"rimraf": "^6.0.1",
|
|
65
78
|
"semantic-release": "^24.2.7",
|
|
66
|
-
"tsdown": "^0.14.
|
|
67
|
-
"typescript": "^5.
|
|
68
|
-
"zod": "^3.25.76"
|
|
79
|
+
"tsdown": "^0.14.1",
|
|
80
|
+
"typescript": "^5.9.2"
|
|
69
81
|
},
|
|
70
82
|
"engines": {
|
|
71
83
|
"node": ">=18.0.0"
|
package/dist/zod-router.cjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=s(require(`express`)),l=s(require(`zod/v3`)),u=s(require(`zod/v4/core`));function d(e){return`_zod`in e}function f(e,t){return d(e)?u.parse(e,t):e.parse(t)}function p(e,t){return d(e)?u.safeParse(e,t):e.safeParse(t)}function m(e){return e instanceof l.ZodError||typeof e==`object`&&!!e&&`issues`in e&&Array.isArray(e.issues)}var h=class{router;constructor(){this.router=c.default.Router()}useMiddleware(e){return this.router.use(e),this}getRouter(){return this.router}get(e,t,n){return this.registerRoute(`get`,e,t,n)}post(e,t,n){return this.registerRoute(`post`,e,t,n)}put(e,t,n){return this.registerRoute(`put`,e,t,n)}patch(e,t,n){return this.registerRoute(`patch`,e,t,n)}delete(e,t,n){return this.registerRoute(`delete`,e,t,n)}options(e,t,n){return this.registerRoute(`options`,e,t,n)}head(e,t,n){return this.registerRoute(`head`,e,t,n)}all(e,t,n){return this.registerRoute(`all`,e,t,n)}registerRoute(e,t,n,r){let i=[];if(typeof n==`object`){let e=n;e.middleware&&i.push(...e.middleware),e.bodySchema&&i.push(this.createBodyValidationMiddleware(e.bodySchema)),e.querySchema&&i.push(this.createQueryValidationMiddleware(e.querySchema)),i.push(r)}else i.push(n);return this.router[e](t,...i),this}createBodyValidationMiddleware(e){return(t,n,r)=>{try{t.body=f(e,t.body),r()}catch(e){m(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):r(e)}}}createQueryValidationMiddleware(e){return(t,n,r)=>{try{let n=f(e,t.query);Object.defineProperty(t,`query`,{value:n,writable:!1,enumerable:!0,configurable:!0}),r()}catch(e){m(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):r(e)}}}};function g(){return new h}function _(e){let t=new h;return e?.errorHandler&&t.getRouter().use(e.errorHandler),t}function v(...e){let t=new h;for(let n of e)t=t.useMiddleware(n);return t}exports.createTypedRouter=g,exports.createTypedRouterWithConfig=_,exports.createTypedRouterWithMiddleware=v,exports.isZod4Schema=d,exports.isZodError=m,exports.parseSchema=f,exports.safeParseSchema=p;
|
package/dist/zod-router.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import e from"express";import*as t from"zod/v3";import*as n from"zod/v4/core";function r(e){return`_zod`in e}function i(e,t){return r(e)?n.parse(e,t):e.parse(t)}function a(e,t){return r(e)?n.safeParse(e,t):e.safeParse(t)}function o(e){return e instanceof t.ZodError||typeof e==`object`&&!!e&&`issues`in e&&Array.isArray(e.issues)}var s=class{router;constructor(){this.router=e.Router()}useMiddleware(e){return this.router.use(e),this}getRouter(){return this.router}get(e,t,n){return this.registerRoute(`get`,e,t,n)}post(e,t,n){return this.registerRoute(`post`,e,t,n)}put(e,t,n){return this.registerRoute(`put`,e,t,n)}patch(e,t,n){return this.registerRoute(`patch`,e,t,n)}delete(e,t,n){return this.registerRoute(`delete`,e,t,n)}options(e,t,n){return this.registerRoute(`options`,e,t,n)}head(e,t,n){return this.registerRoute(`head`,e,t,n)}all(e,t,n){return this.registerRoute(`all`,e,t,n)}registerRoute(e,t,n,r){let i=[];if(typeof n==`object`){let e=n;e.middleware&&i.push(...e.middleware),e.bodySchema&&i.push(this.createBodyValidationMiddleware(e.bodySchema)),e.querySchema&&i.push(this.createQueryValidationMiddleware(e.querySchema)),i.push(r)}else i.push(n);return this.router[e](t,...i),this}createBodyValidationMiddleware(e){return(t,n,r)=>{try{t.body=i(e,t.body),r()}catch(e){o(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):r(e)}}}createQueryValidationMiddleware(e){return(t,n,r)=>{try{let n=i(e,t.query);Object.defineProperty(t,`query`,{value:n,writable:!1,enumerable:!0,configurable:!0}),r()}catch(e){o(e)?n.status(400).json({error:`Validation failed`,details:e.errors||e.issues}):r(e)}}}};function c(){return new s}function l(e){let t=new s;return e?.errorHandler&&t.getRouter().use(e.errorHandler),t}function u(...e){let t=new s;for(let n of e)t=t.useMiddleware(n);return t}export{c as createTypedRouter,l as createTypedRouterWithConfig,u as createTypedRouterWithMiddleware,r as isZod4Schema,o as isZodError,i as parseSchema,a as safeParseSchema};
|