@fishka/express 0.9.13 → 0.9.15
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 +108 -38
- package/dist/cjs/api.types.d.ts +8 -21
- package/dist/cjs/api.types.js +1 -21
- package/dist/cjs/api.types.js.map +1 -1
- package/dist/cjs/error-handling.d.ts +72 -3
- package/dist/cjs/error-handling.js +107 -7
- package/dist/cjs/error-handling.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/route-table.d.ts +15 -7
- package/dist/cjs/route-table.js +3 -0
- package/dist/cjs/route-table.js.map +1 -1
- package/dist/cjs/router.d.ts +25 -33
- package/dist/cjs/router.js +40 -60
- package/dist/cjs/router.js.map +1 -1
- package/dist/cjs/utils/type-validators.d.ts +58 -0
- package/dist/cjs/utils/type-validators.js +122 -0
- package/dist/cjs/utils/type-validators.js.map +1 -0
- package/dist/esm/api.types.d.ts +8 -21
- package/dist/esm/api.types.js +0 -18
- package/dist/esm/api.types.js.map +1 -1
- package/dist/esm/error-handling.d.ts +72 -3
- package/dist/esm/error-handling.js +106 -7
- package/dist/esm/error-handling.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/route-table.d.ts +15 -7
- package/dist/esm/route-table.js +3 -0
- package/dist/esm/route-table.js.map +1 -1
- package/dist/esm/router.d.ts +25 -33
- package/dist/esm/router.js +42 -62
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/utils/type-validators.d.ts +58 -0
- package/dist/esm/utils/type-validators.js +102 -0
- package/dist/esm/utils/type-validators.js.map +1 -0
- package/package.json +1 -1
package/dist/cjs/router.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Assertion, ObjectAssertion } from '@fishka/assertions';
|
|
2
|
-
import { ApiResponse,
|
|
2
|
+
import { ApiResponse, InferValidated, TypedValidatorMap } from './api.types';
|
|
3
3
|
import { AuthUser } from './auth/auth.types';
|
|
4
4
|
import { ExpressRequest, ExpressResponse, ExpressRouter } from './utils/express.utils';
|
|
5
5
|
/** Express API allows handlers to return a response in the raw form. */
|
|
@@ -10,7 +10,7 @@ export type ResponseOrValue<ResponseEntity> = ApiResponse<ResponseEntity> | Resp
|
|
|
10
10
|
*/
|
|
11
11
|
export type EndpointMiddleware<Context = RequestContext> = (run: () => Promise<unknown>, context: Context) => Promise<unknown>;
|
|
12
12
|
/** Generic request context passed to all handlers. Database-agnostic and extensible. */
|
|
13
|
-
export interface RequestContext<Body = void> {
|
|
13
|
+
export interface RequestContext<Body = void, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> {
|
|
14
14
|
/** Parsed and validated request body (for POST/PATCH/PUT handlers). */
|
|
15
15
|
body: Body;
|
|
16
16
|
/** Express Request object. */
|
|
@@ -19,18 +19,10 @@ export interface RequestContext<Body = void> {
|
|
|
19
19
|
res: ExpressResponse;
|
|
20
20
|
/** Authenticated user (if any). Populated by auth middleware. */
|
|
21
21
|
authUser?: AuthUser;
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
params: {
|
|
27
|
-
get(key: string): string;
|
|
28
|
-
tryGet(key: string): string | undefined;
|
|
29
|
-
};
|
|
30
|
-
/** Query parameter access. */
|
|
31
|
-
query: {
|
|
32
|
-
get(key: string): string | undefined;
|
|
33
|
-
};
|
|
22
|
+
/** Validated path parameters (typed from $path validators). */
|
|
23
|
+
path: InferValidated<PathParams>;
|
|
24
|
+
/** Validated query parameters (typed from $query validators). */
|
|
25
|
+
query: InferValidated<QueryParams>;
|
|
34
26
|
/**
|
|
35
27
|
* Generic state storage for middleware to attach data.
|
|
36
28
|
* Allows middleware to pass information to handlers and other middleware.
|
|
@@ -38,47 +30,47 @@ export interface RequestContext<Body = void> {
|
|
|
38
30
|
state: Map<string, unknown>;
|
|
39
31
|
}
|
|
40
32
|
/** Base interface with common endpoint properties. */
|
|
41
|
-
export interface EndpointBase<
|
|
42
|
-
/** Path parameter
|
|
43
|
-
$path?:
|
|
44
|
-
/** Query parameter
|
|
45
|
-
$query?:
|
|
33
|
+
export interface EndpointBase<PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap, Body = void, Result = unknown> {
|
|
34
|
+
/** Path parameter validators (typed). */
|
|
35
|
+
$path?: PathParams;
|
|
36
|
+
/** Query parameter validators (typed). */
|
|
37
|
+
$query?: QueryParams;
|
|
46
38
|
/** Optional middleware to execute before the handler. */
|
|
47
39
|
middlewares?: Array<EndpointMiddleware>;
|
|
48
|
-
/** Handler function. */
|
|
49
|
-
run: (ctx:
|
|
40
|
+
/** Handler function. Can be sync or async. */
|
|
41
|
+
run: (ctx: RequestContext<Body, PathParams, QueryParams>) => ResponseOrValue<Result> | Promise<ResponseOrValue<Result>>;
|
|
50
42
|
}
|
|
51
43
|
/** Descriptor for GET list routes. */
|
|
52
|
-
export type GetListEndpoint<ResultElementType> = EndpointBase<
|
|
44
|
+
export type GetListEndpoint<ResultElementType, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> = EndpointBase<PathParams, QueryParams, void, Array<ResultElementType>>;
|
|
53
45
|
/** Descriptor for GET routes. */
|
|
54
|
-
export type GetEndpoint<Result> = EndpointBase<
|
|
46
|
+
export type GetEndpoint<Result, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> = EndpointBase<PathParams, QueryParams, void, Result>;
|
|
55
47
|
/** Descriptor for POST routes. */
|
|
56
|
-
export interface PostEndpoint<Body, Result = void> extends EndpointBase<
|
|
48
|
+
export interface PostEndpoint<Body, Result = void, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> extends EndpointBase<PathParams, QueryParams, Body, Result> {
|
|
57
49
|
/** Request body validator. */
|
|
58
50
|
$body: Body extends object ? ObjectAssertion<Body> : Assertion<Body>;
|
|
59
51
|
}
|
|
60
52
|
/** Same as POST. Used for full object updates. */
|
|
61
|
-
export type PutEndpoint<Body, Result = void> = PostEndpoint<Body, Result>;
|
|
53
|
+
export type PutEndpoint<Body, Result = void, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> = PostEndpoint<Body, Result, PathParams, QueryParams>;
|
|
62
54
|
/** Same as PUT. While PUT is used for the whole object update, PATCH is used for a partial update. */
|
|
63
|
-
export type PatchEndpoint<Body, Result = void> = PutEndpoint<Body, Result>;
|
|
55
|
+
export type PatchEndpoint<Body, Result = void, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> = PutEndpoint<Body, Result, PathParams, QueryParams>;
|
|
64
56
|
/** Descriptor for DELETE routes. */
|
|
65
|
-
export type DeleteEndpoint = EndpointBase<
|
|
57
|
+
export type DeleteEndpoint<PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> = EndpointBase<PathParams, QueryParams, void, void>;
|
|
66
58
|
/** Union type for all route registration info objects. */
|
|
67
59
|
export type RouteRegistrationInfo = ({
|
|
68
60
|
method: 'get';
|
|
69
|
-
|
|
61
|
+
endpoint: GetEndpoint<unknown> | GetListEndpoint<unknown>;
|
|
70
62
|
} | {
|
|
71
63
|
method: 'post';
|
|
72
|
-
|
|
64
|
+
endpoint: PostEndpoint<unknown>;
|
|
73
65
|
} | {
|
|
74
66
|
method: 'patch';
|
|
75
|
-
|
|
67
|
+
endpoint: PatchEndpoint<unknown>;
|
|
76
68
|
} | {
|
|
77
69
|
method: 'put';
|
|
78
|
-
|
|
70
|
+
endpoint: PutEndpoint<unknown>;
|
|
79
71
|
} | {
|
|
80
72
|
method: 'delete';
|
|
81
|
-
|
|
73
|
+
endpoint: DeleteEndpoint;
|
|
82
74
|
}) & {
|
|
83
75
|
path: string;
|
|
84
76
|
};
|
|
@@ -93,4 +85,4 @@ export declare const mountPut: <Body, Result>(app: ExpressRouter, path: string,
|
|
|
93
85
|
/** Registers a DELETE route. */
|
|
94
86
|
export declare const mountDelete: (app: ExpressRouter, path: string, endpoint: DeleteEndpoint) => void;
|
|
95
87
|
/** Mounts a route with the given method, endpoint, and path. */
|
|
96
|
-
export declare function mount(app: ExpressRouter, { method,
|
|
88
|
+
export declare function mount(app: ExpressRouter, { method, endpoint, path }: RouteRegistrationInfo): void;
|
package/dist/cjs/router.js
CHANGED
|
@@ -46,24 +46,24 @@ const conversion_utils_1 = require("./utils/conversion.utils");
|
|
|
46
46
|
// Internal implementation details
|
|
47
47
|
// ============================================================================
|
|
48
48
|
/** Registers a GET route. */
|
|
49
|
-
const mountGet = (app, path, endpoint) => mount(app, { method: 'get',
|
|
49
|
+
const mountGet = (app, path, endpoint) => mount(app, { method: 'get', endpoint, path });
|
|
50
50
|
exports.mountGet = mountGet;
|
|
51
51
|
/** Registers a POST route. */
|
|
52
|
-
const mountPost = (app, path, endpoint) => mount(app, { method: 'post',
|
|
52
|
+
const mountPost = (app, path, endpoint) => mount(app, { method: 'post', endpoint: endpoint, path });
|
|
53
53
|
exports.mountPost = mountPost;
|
|
54
54
|
/** Registers a PATCH route. */
|
|
55
|
-
const mountPatch = (app, path, endpoint) => mount(app, { method: 'patch',
|
|
55
|
+
const mountPatch = (app, path, endpoint) => mount(app, { method: 'patch', endpoint: endpoint, path });
|
|
56
56
|
exports.mountPatch = mountPatch;
|
|
57
57
|
/** Registers a PUT route. */
|
|
58
|
-
const mountPut = (app, path, endpoint) => mount(app, { method: 'put',
|
|
58
|
+
const mountPut = (app, path, endpoint) => mount(app, { method: 'put', endpoint: endpoint, path });
|
|
59
59
|
exports.mountPut = mountPut;
|
|
60
60
|
/** Registers a DELETE route. */
|
|
61
|
-
const mountDelete = (app, path, endpoint) => mount(app, { method: 'delete',
|
|
61
|
+
const mountDelete = (app, path, endpoint) => mount(app, { method: 'delete', endpoint, path });
|
|
62
62
|
exports.mountDelete = mountDelete;
|
|
63
63
|
/** Mounts a route with the given method, endpoint, and path. */
|
|
64
|
-
function mount(app, { method,
|
|
64
|
+
function mount(app, { method, endpoint, path }) {
|
|
65
65
|
const fullPath = path.startsWith('/') ? path : `/${path}`;
|
|
66
|
-
const handler = createRouteHandler(method,
|
|
66
|
+
const handler = createRouteHandler(method, endpoint);
|
|
67
67
|
app[method](fullPath, (0, error_handling_1.catchRouteErrors)(handler));
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
@@ -98,51 +98,46 @@ function createRouteHandler(method, endpoint) {
|
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
100
|
* @Internal
|
|
101
|
-
* Validates
|
|
101
|
+
* Validates and builds typed path/query parameters using $path and $query validators.
|
|
102
102
|
*/
|
|
103
|
-
function
|
|
103
|
+
function buildValidatedParams(req, $path, $query) {
|
|
104
|
+
const pathResult = {};
|
|
105
|
+
const queryResult = {};
|
|
104
106
|
try {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
(0, assertions_1.callValueAssertion)(value, globalValidator, `${http_status_codes_1.HTTP_BAD_REQUEST}`);
|
|
111
|
-
}
|
|
112
|
-
// Run Local Validation.
|
|
113
|
-
const validator = $path?.[key];
|
|
114
|
-
if (validator) {
|
|
115
|
-
(0, assertions_1.callValueAssertion)(value, validator, `${http_status_codes_1.HTTP_BAD_REQUEST}`);
|
|
107
|
+
// Validate path params
|
|
108
|
+
if ($path) {
|
|
109
|
+
for (const [key, validator] of Object.entries($path)) {
|
|
110
|
+
const value = req.params[key];
|
|
111
|
+
pathResult[key] = validator(value);
|
|
116
112
|
}
|
|
117
113
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// We only validate if it's a single value or handle array in validator.
|
|
126
|
-
// For simplicity, we pass value as is (unknown) to assertion.
|
|
127
|
-
(0, assertions_1.callValueAssertion)(value, globalValidator, `${http_status_codes_1.HTTP_BAD_REQUEST}`);
|
|
128
|
-
}
|
|
129
|
-
const validator = $query?.[key];
|
|
130
|
-
if (validator) {
|
|
131
|
-
(0, assertions_1.callValueAssertion)(value, validator, `${http_status_codes_1.HTTP_BAD_REQUEST}`);
|
|
114
|
+
// Validate query params
|
|
115
|
+
if ($query) {
|
|
116
|
+
const parsedUrl = url.parse(req.url, true);
|
|
117
|
+
for (const [key, validator] of Object.entries($query)) {
|
|
118
|
+
const rawValue = parsedUrl.query[key];
|
|
119
|
+
const value = Array.isArray(rawValue) ? rawValue[0] : rawValue;
|
|
120
|
+
queryResult[key] = validator(value);
|
|
132
121
|
}
|
|
133
122
|
}
|
|
134
123
|
}
|
|
135
124
|
catch (error) {
|
|
125
|
+
if (error instanceof api_types_1.HttpError)
|
|
126
|
+
throw error;
|
|
136
127
|
throw new api_types_1.HttpError(http_status_codes_1.HTTP_BAD_REQUEST, (0, assertions_1.getMessageFromError)(error));
|
|
137
128
|
}
|
|
129
|
+
return {
|
|
130
|
+
path: pathResult,
|
|
131
|
+
query: queryResult,
|
|
132
|
+
};
|
|
138
133
|
}
|
|
139
134
|
/**
|
|
140
135
|
* @Internal
|
|
141
136
|
* Runs GET handler with optional middleware.
|
|
142
137
|
*/
|
|
143
138
|
async function executeGetEndpoint(route, req, res) {
|
|
144
|
-
const
|
|
145
|
-
|
|
139
|
+
const validated = buildValidatedParams(req, route.$path, route.$query);
|
|
140
|
+
const requestContext = newRequestContext(undefined, req, res, validated.path, validated.query);
|
|
146
141
|
return await executeWithMiddleware(() => route.run(requestContext), route.middlewares || [], requestContext);
|
|
147
142
|
}
|
|
148
143
|
/**
|
|
@@ -150,8 +145,8 @@ async function executeGetEndpoint(route, req, res) {
|
|
|
150
145
|
* Runs DELETE handler with optional middleware.
|
|
151
146
|
*/
|
|
152
147
|
async function executeDeleteEndpoint(route, req, res) {
|
|
153
|
-
const
|
|
154
|
-
|
|
148
|
+
const validated = buildValidatedParams(req, route.$path, route.$query);
|
|
149
|
+
const requestContext = newRequestContext(undefined, req, res, validated.path, validated.query);
|
|
155
150
|
await executeWithMiddleware(() => route.run(requestContext), route.middlewares || [], requestContext);
|
|
156
151
|
return undefined;
|
|
157
152
|
}
|
|
@@ -165,12 +160,11 @@ async function executeBodyEndpoint(route, req, res) {
|
|
|
165
160
|
try {
|
|
166
161
|
// Handle validation based on whether the validator is an object or function
|
|
167
162
|
if (typeof validator === 'function') {
|
|
168
|
-
// It's a ValueAssertion (function)
|
|
169
|
-
(
|
|
163
|
+
// It's a ValueAssertion (function) - call it directly
|
|
164
|
+
validator(apiRequest);
|
|
170
165
|
}
|
|
171
166
|
else {
|
|
172
167
|
// It's an ObjectAssertion - use validateObject
|
|
173
|
-
// We strictly assume it is an object because of the type definition (function | object)
|
|
174
168
|
const objectValidator = validator;
|
|
175
169
|
const isEmptyValidator = Object.keys(objectValidator).length === 0;
|
|
176
170
|
const errorMessage = (0, assertions_1.validateObject)(apiRequest, objectValidator, `${http_status_codes_1.HTTP_BAD_REQUEST}: request body`, {
|
|
@@ -184,9 +178,8 @@ async function executeBodyEndpoint(route, req, res) {
|
|
|
184
178
|
throw error;
|
|
185
179
|
throw new api_types_1.HttpError(http_status_codes_1.HTTP_BAD_REQUEST, (0, assertions_1.getMessageFromError)(error));
|
|
186
180
|
}
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
requestContext.body = req.body;
|
|
181
|
+
const validated = buildValidatedParams(req, route.$path, route.$query);
|
|
182
|
+
const requestContext = newRequestContext(apiRequest, req, res, validated.path, validated.query);
|
|
190
183
|
return await executeWithMiddleware(() => route.run(requestContext), (route.middlewares || []), requestContext);
|
|
191
184
|
}
|
|
192
185
|
/**
|
|
@@ -208,26 +201,13 @@ async function executeWithMiddleware(run, middlewares, context) {
|
|
|
208
201
|
* @Internal
|
|
209
202
|
* Creates a new RequestContext instance.
|
|
210
203
|
*/
|
|
211
|
-
function newRequestContext(requestBody, req, res) {
|
|
204
|
+
function newRequestContext(requestBody, req, res, validatedPath, validatedQuery) {
|
|
212
205
|
return {
|
|
213
206
|
body: requestBody,
|
|
214
207
|
req,
|
|
215
208
|
res,
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const value = req.params[key];
|
|
219
|
-
(0, api_types_1.assertHttp)(value, http_status_codes_1.HTTP_BAD_REQUEST, `Path parameter '${key}' not found`);
|
|
220
|
-
return value;
|
|
221
|
-
},
|
|
222
|
-
tryGet: (key) => req.params[key],
|
|
223
|
-
},
|
|
224
|
-
query: {
|
|
225
|
-
get: (key) => {
|
|
226
|
-
const parsedUrl = url.parse(req.url, true);
|
|
227
|
-
const value = parsedUrl.query[key];
|
|
228
|
-
return Array.isArray(value) ? value[0] : value;
|
|
229
|
-
},
|
|
230
|
-
},
|
|
209
|
+
path: validatedPath,
|
|
210
|
+
query: validatedQuery,
|
|
231
211
|
state: new Map(),
|
|
232
212
|
};
|
|
233
213
|
}
|
package/dist/cjs/router.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/router.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/router.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL parameter validators for Express path/query params.
|
|
3
|
+
* All params are strings from Express, so validation starts with string.
|
|
4
|
+
*/
|
|
5
|
+
import { TypeValidator } from '../api.types';
|
|
6
|
+
/** Makes validator optional - returns undefined if value missing */
|
|
7
|
+
export declare function optional<T>(validator: TypeValidator<T>): TypeValidator<T | undefined>;
|
|
8
|
+
/** Operator that transforms string to T */
|
|
9
|
+
export type ParamOperator<T> = (value: string) => T;
|
|
10
|
+
/** Operator that transforms T to R */
|
|
11
|
+
export type Operator<T, R = T> = (value: T) => R;
|
|
12
|
+
/**
|
|
13
|
+
* Creates a URL parameter validator. Input is always string (from Express).
|
|
14
|
+
* Operators are applied in sequence to validate/transform the value.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* param() // string
|
|
18
|
+
* param(toInt) // number
|
|
19
|
+
* param(toInt, min(1)) // number >= 1
|
|
20
|
+
* param(minLength(3)) // string with min length
|
|
21
|
+
* param(trim, lowercase) // trimmed lowercase string
|
|
22
|
+
*/
|
|
23
|
+
export declare function param(): TypeValidator<string>;
|
|
24
|
+
export declare function param<A>(op1: ParamOperator<A>): TypeValidator<A>;
|
|
25
|
+
export declare function param<A, B>(op1: ParamOperator<A>, op2: Operator<A, B>): TypeValidator<B>;
|
|
26
|
+
export declare function param<A, B, C>(op1: ParamOperator<A>, op2: Operator<A, B>, op3: Operator<B, C>): TypeValidator<C>;
|
|
27
|
+
export declare function param<A, B, C, D>(op1: ParamOperator<A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>): TypeValidator<D>;
|
|
28
|
+
export declare function param<A, B, C, D, E>(op1: ParamOperator<A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>): TypeValidator<E>;
|
|
29
|
+
/** Parses string to integer */
|
|
30
|
+
export declare const toInt: (message?: string) => ParamOperator<number>;
|
|
31
|
+
/** Parses string to number */
|
|
32
|
+
export declare const toNumber: (message?: string) => ParamOperator<number>;
|
|
33
|
+
/** Parses string to boolean ('true'/'false') */
|
|
34
|
+
export declare const toBool: (message?: string) => ParamOperator<boolean>;
|
|
35
|
+
/** Validates value is one of allowed enum values */
|
|
36
|
+
export declare const oneOf: <T extends string>(...allowedValues: T[]) => ParamOperator<T>;
|
|
37
|
+
/** Requires minimum string length */
|
|
38
|
+
export declare const minLength: (n: number, message?: string) => ParamOperator<string>;
|
|
39
|
+
/** Requires maximum string length */
|
|
40
|
+
export declare const maxLength: (n: number, message?: string) => ParamOperator<string>;
|
|
41
|
+
/** Requires string to match regex */
|
|
42
|
+
export declare const matches: (regex: RegExp, message?: string) => ParamOperator<string>;
|
|
43
|
+
/** Trims whitespace from string */
|
|
44
|
+
export declare const trim: ParamOperator<string>;
|
|
45
|
+
/** Converts string to lowercase */
|
|
46
|
+
export declare const lowercase: ParamOperator<string>;
|
|
47
|
+
/** Converts string to uppercase */
|
|
48
|
+
export declare const uppercase: ParamOperator<string>;
|
|
49
|
+
/** Requires minimum value */
|
|
50
|
+
export declare const min: (n: number, message?: string) => Operator<number>;
|
|
51
|
+
/** Requires maximum value */
|
|
52
|
+
export declare const max: (n: number, message?: string) => Operator<number>;
|
|
53
|
+
/** Requires value to be in range [minVal, maxVal] */
|
|
54
|
+
export declare const range: (minVal: number, maxVal: number, message?: string) => Operator<number>;
|
|
55
|
+
/** Adds custom validation */
|
|
56
|
+
export declare const check: <T>(predicate: (value: T) => boolean, message: string) => Operator<T>;
|
|
57
|
+
/** Transforms the value */
|
|
58
|
+
export declare const map: <T, R>(fn: (value: T) => R) => Operator<T, R>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* URL parameter validators for Express path/query params.
|
|
4
|
+
* All params are strings from Express, so validation starts with string.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.map = exports.check = exports.range = exports.max = exports.min = exports.uppercase = exports.lowercase = exports.trim = exports.matches = exports.maxLength = exports.minLength = exports.oneOf = exports.toBool = exports.toNumber = exports.toInt = void 0;
|
|
8
|
+
exports.optional = optional;
|
|
9
|
+
exports.param = param;
|
|
10
|
+
const assertions_1 = require("@fishka/assertions");
|
|
11
|
+
/** Makes validator optional - returns undefined if value missing */
|
|
12
|
+
function optional(validator) {
|
|
13
|
+
return (value) => {
|
|
14
|
+
if (value === undefined || value === null || value === '') {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
return validator(value);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function param(...operators) {
|
|
21
|
+
return (value) => {
|
|
22
|
+
(0, assertions_1.assertTruthy)(typeof value === 'string', `Expected string, got ${typeof value}`);
|
|
23
|
+
let result = value;
|
|
24
|
+
for (const op of operators) {
|
|
25
|
+
result = op(result);
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// String → T operators (first in chain)
|
|
32
|
+
// ============================================================================
|
|
33
|
+
/** Parses string to integer */
|
|
34
|
+
const toInt = (message) => (value) => {
|
|
35
|
+
const num = Number(value);
|
|
36
|
+
(0, assertions_1.assertTruthy)(Number.isInteger(num), message ?? `Expected integer, got '${value}'`);
|
|
37
|
+
return num;
|
|
38
|
+
};
|
|
39
|
+
exports.toInt = toInt;
|
|
40
|
+
/** Parses string to number */
|
|
41
|
+
const toNumber = (message) => (value) => {
|
|
42
|
+
const num = Number(value);
|
|
43
|
+
(0, assertions_1.assertTruthy)(!isNaN(num), message ?? `Expected number, got '${value}'`);
|
|
44
|
+
return num;
|
|
45
|
+
};
|
|
46
|
+
exports.toNumber = toNumber;
|
|
47
|
+
/** Parses string to boolean ('true'/'false') */
|
|
48
|
+
const toBool = (message) => (value) => {
|
|
49
|
+
(0, assertions_1.assertTruthy)(value === 'true' || value === 'false', message ?? `Expected 'true' or 'false', got '${value}'`);
|
|
50
|
+
return value === 'true';
|
|
51
|
+
};
|
|
52
|
+
exports.toBool = toBool;
|
|
53
|
+
/** Validates value is one of allowed enum values */
|
|
54
|
+
const oneOf = (...allowedValues) => (value) => {
|
|
55
|
+
(0, assertions_1.assertTruthy)(allowedValues.includes(value), `Expected one of [${allowedValues.join(', ')}], got '${value}'`);
|
|
56
|
+
return value;
|
|
57
|
+
};
|
|
58
|
+
exports.oneOf = oneOf;
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// String operators (string → string)
|
|
61
|
+
// ============================================================================
|
|
62
|
+
/** Requires minimum string length */
|
|
63
|
+
const minLength = (n, message) => (value) => {
|
|
64
|
+
(0, assertions_1.assertTruthy)(value.length >= n, message ?? `Must be at least ${n} characters`);
|
|
65
|
+
return value;
|
|
66
|
+
};
|
|
67
|
+
exports.minLength = minLength;
|
|
68
|
+
/** Requires maximum string length */
|
|
69
|
+
const maxLength = (n, message) => (value) => {
|
|
70
|
+
(0, assertions_1.assertTruthy)(value.length <= n, message ?? `Must be at most ${n} characters`);
|
|
71
|
+
return value;
|
|
72
|
+
};
|
|
73
|
+
exports.maxLength = maxLength;
|
|
74
|
+
/** Requires string to match regex */
|
|
75
|
+
const matches = (regex, message) => (value) => {
|
|
76
|
+
(0, assertions_1.assertTruthy)(regex.test(value), message ?? `Must match pattern ${regex}`);
|
|
77
|
+
return value;
|
|
78
|
+
};
|
|
79
|
+
exports.matches = matches;
|
|
80
|
+
/** Trims whitespace from string */
|
|
81
|
+
const trim = (value) => value.trim();
|
|
82
|
+
exports.trim = trim;
|
|
83
|
+
/** Converts string to lowercase */
|
|
84
|
+
const lowercase = (value) => value.toLowerCase();
|
|
85
|
+
exports.lowercase = lowercase;
|
|
86
|
+
/** Converts string to uppercase */
|
|
87
|
+
const uppercase = (value) => value.toUpperCase();
|
|
88
|
+
exports.uppercase = uppercase;
|
|
89
|
+
// ============================================================================
|
|
90
|
+
// Number operators (number → number)
|
|
91
|
+
// ============================================================================
|
|
92
|
+
/** Requires minimum value */
|
|
93
|
+
const min = (n, message) => (value) => {
|
|
94
|
+
(0, assertions_1.assertTruthy)(value >= n, message ?? `Must be at least ${n}`);
|
|
95
|
+
return value;
|
|
96
|
+
};
|
|
97
|
+
exports.min = min;
|
|
98
|
+
/** Requires maximum value */
|
|
99
|
+
const max = (n, message) => (value) => {
|
|
100
|
+
(0, assertions_1.assertTruthy)(value <= n, message ?? `Must be at most ${n}`);
|
|
101
|
+
return value;
|
|
102
|
+
};
|
|
103
|
+
exports.max = max;
|
|
104
|
+
/** Requires value to be in range [minVal, maxVal] */
|
|
105
|
+
const range = (minVal, maxVal, message) => (value) => {
|
|
106
|
+
(0, assertions_1.assertTruthy)(value >= minVal && value <= maxVal, message ?? `Must be between ${minVal} and ${maxVal}`);
|
|
107
|
+
return value;
|
|
108
|
+
};
|
|
109
|
+
exports.range = range;
|
|
110
|
+
// ============================================================================
|
|
111
|
+
// Generic operators
|
|
112
|
+
// ============================================================================
|
|
113
|
+
/** Adds custom validation */
|
|
114
|
+
const check = (predicate, message) => (value) => {
|
|
115
|
+
(0, assertions_1.assertTruthy)(predicate(value), message);
|
|
116
|
+
return value;
|
|
117
|
+
};
|
|
118
|
+
exports.check = check;
|
|
119
|
+
/** Transforms the value */
|
|
120
|
+
const map = (fn) => (value) => fn(value);
|
|
121
|
+
exports.map = map;
|
|
122
|
+
//# sourceMappingURL=type-validators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-validators.js","sourceRoot":"","sources":["../../../src/utils/type-validators.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAMH,4BAOC;AAwCD,sBASC;AA5DD,mDAAkD;AAGlD,oEAAoE;AACpE,SAAgB,QAAQ,CAAI,SAA2B;IACrD,OAAO,CAAC,KAAc,EAAiB,EAAE;QACvC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YAC1D,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC,CAAC;AACJ,CAAC;AAwCD,SAAgB,KAAK,CAAC,GAAG,SAA2C;IAClE,OAAO,CAAC,KAAc,EAAW,EAAE;QACjC,IAAA,yBAAY,EAAC,OAAO,KAAK,KAAK,QAAQ,EAAE,wBAAwB,OAAO,KAAK,EAAE,CAAC,CAAC;QAChF,IAAI,MAAM,GAAY,KAAK,CAAC;QAC5B,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;YAC3B,MAAM,GAAG,EAAE,CAAC,MAAe,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,wCAAwC;AACxC,+EAA+E;AAE/E,+BAA+B;AACxB,MAAM,KAAK,GAChB,CAAC,OAAgB,EAAyB,EAAE,CAC5C,CAAC,KAAa,EAAU,EAAE;IACxB,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAA,yBAAY,EAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,OAAO,IAAI,0BAA0B,KAAK,GAAG,CAAC,CAAC;IACnF,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AANS,QAAA,KAAK,SAMd;AAEJ,8BAA8B;AACvB,MAAM,QAAQ,GACnB,CAAC,OAAgB,EAAyB,EAAE,CAC5C,CAAC,KAAa,EAAU,EAAE;IACxB,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAA,yBAAY,EAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,IAAI,yBAAyB,KAAK,GAAG,CAAC,CAAC;IACxE,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AANS,QAAA,QAAQ,YAMjB;AAEJ,gDAAgD;AACzC,MAAM,MAAM,GACjB,CAAC,OAAgB,EAA0B,EAAE,CAC7C,CAAC,KAAa,EAAW,EAAE;IACzB,IAAA,yBAAY,EAAC,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO,EAAE,OAAO,IAAI,oCAAoC,KAAK,GAAG,CAAC,CAAC;IAC7G,OAAO,KAAK,KAAK,MAAM,CAAC;AAC1B,CAAC,CAAC;AALS,QAAA,MAAM,UAKf;AAEJ,oDAAoD;AAC7C,MAAM,KAAK,GAChB,CAAmB,GAAG,aAAkB,EAAoB,EAAE,CAC9D,CAAC,KAAa,EAAK,EAAE;IACnB,IAAA,yBAAY,EAAC,aAAa,CAAC,QAAQ,CAAC,KAAU,CAAC,EAAE,oBAAoB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,KAAK,GAAG,CAAC,CAAC;IAClH,OAAO,KAAU,CAAC;AACpB,CAAC,CAAC;AALS,QAAA,KAAK,SAKd;AAEJ,+EAA+E;AAC/E,qCAAqC;AACrC,+EAA+E;AAE/E,qCAAqC;AAC9B,MAAM,SAAS,GACpB,CAAC,CAAS,EAAE,OAAgB,EAAyB,EAAE,CACvD,CAAC,KAAa,EAAU,EAAE;IACxB,IAAA,yBAAY,EAAC,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,OAAO,IAAI,oBAAoB,CAAC,aAAa,CAAC,CAAC;IAC/E,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AALS,QAAA,SAAS,aAKlB;AAEJ,qCAAqC;AAC9B,MAAM,SAAS,GACpB,CAAC,CAAS,EAAE,OAAgB,EAAyB,EAAE,CACvD,CAAC,KAAa,EAAU,EAAE;IACxB,IAAA,yBAAY,EAAC,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,OAAO,IAAI,mBAAmB,CAAC,aAAa,CAAC,CAAC;IAC9E,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AALS,QAAA,SAAS,aAKlB;AAEJ,qCAAqC;AAC9B,MAAM,OAAO,GAClB,CAAC,KAAa,EAAE,OAAgB,EAAyB,EAAE,CAC3D,CAAC,KAAa,EAAU,EAAE;IACxB,IAAA,yBAAY,EAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,IAAI,sBAAsB,KAAK,EAAE,CAAC,CAAC;IAC1E,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AALS,QAAA,OAAO,WAKhB;AAEJ,mCAAmC;AAC5B,MAAM,IAAI,GAA0B,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;AAAtE,QAAA,IAAI,QAAkE;AAEnF,mCAAmC;AAC5B,MAAM,SAAS,GAA0B,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;AAAlF,QAAA,SAAS,aAAyE;AAE/F,mCAAmC;AAC5B,MAAM,SAAS,GAA0B,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;AAAlF,QAAA,SAAS,aAAyE;AAE/F,+EAA+E;AAC/E,qCAAqC;AACrC,+EAA+E;AAE/E,6BAA6B;AACtB,MAAM,GAAG,GACd,CAAC,CAAS,EAAE,OAAgB,EAAoB,EAAE,CAClD,CAAC,KAAa,EAAU,EAAE;IACxB,IAAA,yBAAY,EAAC,KAAK,IAAI,CAAC,EAAE,OAAO,IAAI,oBAAoB,CAAC,EAAE,CAAC,CAAC;IAC7D,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AALS,QAAA,GAAG,OAKZ;AAEJ,6BAA6B;AACtB,MAAM,GAAG,GACd,CAAC,CAAS,EAAE,OAAgB,EAAoB,EAAE,CAClD,CAAC,KAAa,EAAU,EAAE;IACxB,IAAA,yBAAY,EAAC,KAAK,IAAI,CAAC,EAAE,OAAO,IAAI,mBAAmB,CAAC,EAAE,CAAC,CAAC;IAC5D,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AALS,QAAA,GAAG,OAKZ;AAEJ,qDAAqD;AAC9C,MAAM,KAAK,GAChB,CAAC,MAAc,EAAE,MAAc,EAAE,OAAgB,EAAoB,EAAE,CACvE,CAAC,KAAa,EAAU,EAAE;IACxB,IAAA,yBAAY,EAAC,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,EAAE,OAAO,IAAI,mBAAmB,MAAM,QAAQ,MAAM,EAAE,CAAC,CAAC;IACvG,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AALS,QAAA,KAAK,SAKd;AAEJ,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E,6BAA6B;AACtB,MAAM,KAAK,GAChB,CAAI,SAAgC,EAAE,OAAe,EAAe,EAAE,CACtE,CAAC,KAAQ,EAAK,EAAE;IACd,IAAA,yBAAY,EAAC,SAAS,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;IACxC,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AALS,QAAA,KAAK,SAKd;AAEJ,2BAA2B;AACpB,MAAM,GAAG,GACd,CAAO,EAAmB,EAAkB,EAAE,CAC9C,CAAC,KAAQ,EAAK,EAAE,CACd,EAAE,CAAC,KAAK,CAAC,CAAC;AAHD,QAAA,GAAG,OAGF"}
|
package/dist/esm/api.types.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
export type
|
|
1
|
+
/** Validator function that validates and returns typed value */
|
|
2
|
+
export type TypeValidator<T> = (value: unknown) => T;
|
|
3
|
+
/** Map of param name to type validator */
|
|
4
|
+
export type TypedValidatorMap = Record<string, TypeValidator<unknown>>;
|
|
5
|
+
/** Infer validated types from validator map */
|
|
6
|
+
export type InferValidated<T extends TypedValidatorMap | undefined> = T extends TypedValidatorMap ? {
|
|
7
|
+
[K in keyof T]: ReturnType<T[K]>;
|
|
8
|
+
} : Record<string, never>;
|
|
3
9
|
export declare class HttpError extends Error {
|
|
4
10
|
readonly status: number;
|
|
5
11
|
readonly details?: Record<string, unknown> | undefined;
|
|
@@ -62,22 +68,3 @@ export interface ApiResponse<ResponseEntity = unknown> {
|
|
|
62
68
|
}
|
|
63
69
|
/** Converts an API response value into a standardized ApiResponse structure. */
|
|
64
70
|
export declare function response<T = unknown>(result: T): ApiResponse<T>;
|
|
65
|
-
/** Globally identified URL (path or query) parameter info. */
|
|
66
|
-
export interface UrlParameterInfo {
|
|
67
|
-
/** Optional global validator for this parameter. */
|
|
68
|
-
validator?: ValueAssertion<string>;
|
|
69
|
-
/** Description for documentation. */
|
|
70
|
-
description?: string;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Default documentation and validation for URL parameters.
|
|
74
|
-
* @Internal
|
|
75
|
-
*/
|
|
76
|
-
export declare const URL_PARAMETER_INFO: Record<string, UrlParameterInfo>;
|
|
77
|
-
/** Registers a new URL parameter. */
|
|
78
|
-
export declare function registerUrlParameter(name: string, info: UrlParameterInfo): void;
|
|
79
|
-
/**
|
|
80
|
-
* Asserts that the value is a registered URL parameter name.
|
|
81
|
-
* @Internal
|
|
82
|
-
*/
|
|
83
|
-
export declare function assertUrlParameter(name: unknown): asserts name is string;
|
package/dist/esm/api.types.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { assertTruthy } from '@fishka/assertions';
|
|
2
|
-
import { HTTP_BAD_REQUEST } from './http-status-codes';
|
|
3
2
|
export class HttpError extends Error {
|
|
4
3
|
constructor(status, message, details) {
|
|
5
4
|
super(message);
|
|
@@ -47,21 +46,4 @@ export function assertHttp(value, status, message) {
|
|
|
47
46
|
export function response(result) {
|
|
48
47
|
return { result };
|
|
49
48
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Default documentation and validation for URL parameters.
|
|
52
|
-
* @Internal
|
|
53
|
-
*/
|
|
54
|
-
export const URL_PARAMETER_INFO = {};
|
|
55
|
-
/** Registers a new URL parameter. */
|
|
56
|
-
export function registerUrlParameter(name, info) {
|
|
57
|
-
URL_PARAMETER_INFO[name] = info;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Asserts that the value is a registered URL parameter name.
|
|
61
|
-
* @Internal
|
|
62
|
-
*/
|
|
63
|
-
export function assertUrlParameter(name) {
|
|
64
|
-
assertHttp(typeof name === 'string', HTTP_BAD_REQUEST, 'Url parameter name must be a string');
|
|
65
|
-
assertHttp(URL_PARAMETER_INFO[name], HTTP_BAD_REQUEST, `Invalid URL parameter: '${name}'. Please register it using 'registerUrlParameter('${name}', ...)'`);
|
|
66
|
-
}
|
|
67
49
|
//# sourceMappingURL=api.types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.types.js","sourceRoot":"","sources":["../../src/api.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"api.types.js","sourceRoot":"","sources":["../../src/api.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAclD,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC,YACkB,MAAc,EAC9B,OAAe,EACC,OAAiC;QAEjD,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,WAAM,GAAN,MAAM,CAAQ;QAEd,YAAO,GAAP,OAAO,CAA0B;QAGjD,qDAAqD;QACrD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnD,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,UAAU,CAAC,KAAc,EAAE,MAAc,EAAE,OAAe;IACxE,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC5D,CAAC;AA0BD,gFAAgF;AAChF,MAAM,UAAU,QAAQ,CAAc,MAAS;IAC7C,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC"}
|