@fishka/express 0.9.14 → 0.9.16
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 +87 -39
- 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/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 +14 -6
- package/dist/cjs/route-table.js +3 -0
- package/dist/cjs/route-table.js.map +1 -1
- package/dist/cjs/router.d.ts +18 -26
- package/dist/cjs/router.js +33 -53
- 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/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 +14 -6
- package/dist/esm/route-table.js +3 -0
- package/dist/esm/route-table.js.map +1 -1
- package/dist/esm/router.d.ts +18 -26
- package/dist/esm/router.js +35 -55
- 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/README.md
CHANGED
|
@@ -12,56 +12,105 @@ npm install @fishka/express
|
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
14
|
import express from 'express';
|
|
15
|
-
import { createRouteTable } from '@fishka/express';
|
|
15
|
+
import { createRouteTable, param, toInt } from '@fishka/express';
|
|
16
|
+
import { assertString } from '@fishka/assertions';
|
|
16
17
|
|
|
17
18
|
const app = express();
|
|
18
19
|
app.use(express.json());
|
|
19
20
|
|
|
20
21
|
const routes = createRouteTable(app);
|
|
21
22
|
|
|
22
|
-
// GET /users/:id -
|
|
23
|
-
routes.get
|
|
24
|
-
id:
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
// GET /users/:id - with typed path params
|
|
24
|
+
routes.get('users/:id', {
|
|
25
|
+
$path: { id: param(toInt()) },
|
|
26
|
+
run: async ctx => ({
|
|
27
|
+
id: ctx.path.id, // number - typed from $path
|
|
28
|
+
name: 'John',
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
27
31
|
|
|
28
|
-
// GET /users -
|
|
29
|
-
routes.get
|
|
30
|
-
{ id:
|
|
31
|
-
{ id:
|
|
32
|
+
// GET /users - list all users
|
|
33
|
+
routes.get('users', async () => [
|
|
34
|
+
{ id: 1, name: 'John' },
|
|
35
|
+
{ id: 2, name: 'Jane' },
|
|
32
36
|
]);
|
|
33
37
|
|
|
34
|
-
// POST /users
|
|
35
|
-
routes.post<{ name: string }, { id:
|
|
38
|
+
// POST /users - with body validation
|
|
39
|
+
routes.post<{ name: string }, { id: number }>('users', {
|
|
36
40
|
$body: { name: v => assertString(v, 'name required') },
|
|
37
|
-
run: async ctx => ({ id:
|
|
41
|
+
run: async ctx => ({ id: 1 }),
|
|
38
42
|
});
|
|
39
43
|
|
|
40
|
-
// DELETE /users/:id
|
|
41
|
-
routes.delete('users/:id', async () => {
|
|
42
|
-
// Delete user logic
|
|
43
|
-
});
|
|
44
|
+
// DELETE /users/:id
|
|
45
|
+
routes.delete('users/:id', async () => {});
|
|
44
46
|
|
|
45
47
|
app.listen(3000);
|
|
46
48
|
```
|
|
47
49
|
|
|
48
|
-
## URL
|
|
50
|
+
## URL Parameter Validation
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
Use `param()` to validate and transform path/query parameters. All operators are composable:
|
|
51
53
|
|
|
52
54
|
```typescript
|
|
53
|
-
import {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
validator: val => {
|
|
59
|
-
assertString(val);
|
|
60
|
-
assertTruthy(val.startsWith('org-'), 'Invalid Organization ID');
|
|
55
|
+
import { param, toInt, minLength, matches, min, range, oneOf } from '@fishka/express';
|
|
56
|
+
|
|
57
|
+
routes.get('users/:id', {
|
|
58
|
+
$path: {
|
|
59
|
+
id: param(toInt()), // string → number
|
|
61
60
|
},
|
|
61
|
+
$query: {
|
|
62
|
+
page: param(toInt(), min(1)), // number >= 1
|
|
63
|
+
limit: param(toInt(), range(1, 100)), // number 1-100
|
|
64
|
+
sort: param(oneOf('asc', 'desc')), // enum
|
|
65
|
+
search: param(minLength(3)), // string min 3 chars
|
|
66
|
+
},
|
|
67
|
+
run: async ctx => ({
|
|
68
|
+
id: ctx.path.id, // number
|
|
69
|
+
page: ctx.query.page, // number
|
|
70
|
+
sort: ctx.query.sort, // 'asc' | 'desc'
|
|
71
|
+
}),
|
|
62
72
|
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Available Operators
|
|
76
|
+
|
|
77
|
+
**Transformations (string → T):**
|
|
78
|
+
- `toInt()` - parse to integer
|
|
79
|
+
- `toNumber()` - parse to number
|
|
80
|
+
- `toBool()` - parse 'true'/'false' to boolean
|
|
81
|
+
- `oneOf('a', 'b')` - enum values
|
|
82
|
+
|
|
83
|
+
**String validators:**
|
|
84
|
+
- `minLength(n)` - minimum length
|
|
85
|
+
- `maxLength(n)` - maximum length
|
|
86
|
+
- `matches(/regex/)` - regex match
|
|
87
|
+
- `trim` - trim whitespace
|
|
88
|
+
- `lowercase` / `uppercase` - case transform
|
|
89
|
+
|
|
90
|
+
**Number validators:**
|
|
91
|
+
- `min(n)` - minimum value
|
|
92
|
+
- `max(n)` - maximum value
|
|
93
|
+
- `range(min, max)` - value range
|
|
94
|
+
|
|
95
|
+
**Generic:**
|
|
96
|
+
- `check(fn, msg)` - custom validation
|
|
97
|
+
- `map(fn)` - transform value
|
|
98
|
+
|
|
99
|
+
### Optional Parameters
|
|
63
100
|
|
|
64
|
-
|
|
101
|
+
Use `optional()` to make parameters optional:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { optional, param, toInt } from '@fishka/express';
|
|
105
|
+
|
|
106
|
+
routes.get('users', {
|
|
107
|
+
$query: {
|
|
108
|
+
page: optional(param(toInt())), // number | undefined
|
|
109
|
+
},
|
|
110
|
+
run: async ctx => {
|
|
111
|
+
const page = ctx.query.page ?? 1;
|
|
112
|
+
},
|
|
113
|
+
});
|
|
65
114
|
```
|
|
66
115
|
|
|
67
116
|
## Authentication
|
|
@@ -110,12 +159,11 @@ assertHttp(user.isAdmin, HTTP_FORBIDDEN, 'Admin access required');
|
|
|
110
159
|
|
|
111
160
|
## Complete Example
|
|
112
161
|
|
|
113
|
-
|
|
162
|
+
Full initialization with TLS context, validation, and error handling:
|
|
114
163
|
|
|
115
164
|
```typescript
|
|
116
165
|
import express from 'express';
|
|
117
|
-
import { createRouteTable, createTlsMiddleware, catchAllMiddleware,
|
|
118
|
-
import { assertString, assertTruthy } from '@fishka/assertions';
|
|
166
|
+
import { createRouteTable, createTlsMiddleware, catchAllMiddleware, param, toInt } from '@fishka/express';
|
|
119
167
|
|
|
120
168
|
const app = express();
|
|
121
169
|
|
|
@@ -125,17 +173,17 @@ app.use(express.json());
|
|
|
125
173
|
// 2. Initialize TLS context (Request IDs, etc.)
|
|
126
174
|
app.use(createTlsMiddleware());
|
|
127
175
|
|
|
128
|
-
// 3.
|
|
129
|
-
registerUrlParameter('id', {
|
|
130
|
-
validator: val => assertString(val),
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// 4. Define routes
|
|
176
|
+
// 3. Define routes with typed parameters
|
|
134
177
|
const routes = createRouteTable(app);
|
|
178
|
+
|
|
135
179
|
routes.get('health', async () => ({ status: 'UP' }));
|
|
136
180
|
|
|
137
|
-
|
|
138
|
-
|
|
181
|
+
routes.get('users/:id', {
|
|
182
|
+
$path: { id: param(toInt()) },
|
|
183
|
+
run: async ctx => ({ id: ctx.path.id }),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// 4. Error handler - catches middleware/parsing errors
|
|
139
187
|
app.use(catchAllMiddleware);
|
|
140
188
|
|
|
141
189
|
app.listen(3000);
|
package/dist/cjs/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, string>;
|
|
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/cjs/api.types.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.HttpError = void 0;
|
|
4
4
|
exports.assertHttp = assertHttp;
|
|
5
5
|
exports.response = response;
|
|
6
|
-
exports.registerUrlParameter = registerUrlParameter;
|
|
7
|
-
exports.assertUrlParameter = assertUrlParameter;
|
|
8
6
|
const assertions_1 = require("@fishka/assertions");
|
|
9
|
-
const http_status_codes_1 = require("./http-status-codes");
|
|
10
7
|
class HttpError extends Error {
|
|
11
8
|
constructor(status, message, details) {
|
|
12
9
|
super(message);
|
|
@@ -55,21 +52,4 @@ function assertHttp(value, status, message) {
|
|
|
55
52
|
function response(result) {
|
|
56
53
|
return { result };
|
|
57
54
|
}
|
|
58
|
-
/**
|
|
59
|
-
* Default documentation and validation for URL parameters.
|
|
60
|
-
* @Internal
|
|
61
|
-
*/
|
|
62
|
-
exports.URL_PARAMETER_INFO = {};
|
|
63
|
-
/** Registers a new URL parameter. */
|
|
64
|
-
function registerUrlParameter(name, info) {
|
|
65
|
-
exports.URL_PARAMETER_INFO[name] = info;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Asserts that the value is a registered URL parameter name.
|
|
69
|
-
* @Internal
|
|
70
|
-
*/
|
|
71
|
-
function assertUrlParameter(name) {
|
|
72
|
-
assertHttp(typeof name === 'string', http_status_codes_1.HTTP_BAD_REQUEST, 'Url parameter name must be a string');
|
|
73
|
-
assertHttp(exports.URL_PARAMETER_INFO[name], http_status_codes_1.HTTP_BAD_REQUEST, `Invalid URL parameter: '${name}'. Please register it using 'registerUrlParameter('${name}', ...)'`);
|
|
74
|
-
}
|
|
75
55
|
//# sourceMappingURL=api.types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.types.js","sourceRoot":"","sources":["../../src/api.types.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"api.types.js","sourceRoot":"","sources":["../../src/api.types.ts"],"names":[],"mappings":";;;AAyDA,gCAEC;AA2BD,4BAEC;AAxFD,mDAAkD;AAclD,MAAa,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;AAVD,8BAUC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,SAAgB,UAAU,CAAC,KAAc,EAAE,MAAc,EAAE,OAAe;IACxE,IAAA,yBAAY,EAAC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC5D,CAAC;AA0BD,gFAAgF;AAChF,SAAgB,QAAQ,CAAc,MAAS;IAC7C,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC"}
|
package/dist/cjs/index.d.ts
CHANGED
package/dist/cjs/index.js
CHANGED
|
@@ -29,4 +29,5 @@ __exportStar(require("./router"), exports);
|
|
|
29
29
|
__exportStar(require("./thread-local/thread-local-storage"), exports);
|
|
30
30
|
__exportStar(require("./thread-local/thread-local-storage-middleware"), exports);
|
|
31
31
|
__exportStar(require("./utils/express.utils"), exports);
|
|
32
|
+
__exportStar(require("./utils/type-validators"), exports);
|
|
32
33
|
//# sourceMappingURL=index.js.map
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA4B;AAC5B,uDAAqC;AACrC,oDAAkC;AAClC,oDAAkC;AAClC,8DAA4C;AAC5C,2CAAyB;AACzB,mDAAiC;AACjC,sDAAoC;AACpC,sEAAoD;AACpD,gEAA8C;AAC9C,gDAA8B;AAC9B,2CAAyB;AACzB,sEAAoD;AACpD,iFAA+D;AAC/D,wDAAsC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA4B;AAC5B,uDAAqC;AACrC,oDAAkC;AAClC,oDAAkC;AAClC,8DAA4C;AAC5C,2CAAyB;AACzB,mDAAiC;AACjC,sDAAoC;AACpC,sEAAoD;AACpD,gEAA8C;AAC9C,gDAA8B;AAC9B,2CAAyB;AACzB,sEAAoD;AACpD,iFAA+D;AAC/D,wDAAsC;AACtC,0DAAwC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TypedValidatorMap } from './api.types';
|
|
1
2
|
import { DeleteEndpoint, GetEndpoint, PatchEndpoint, PostEndpoint, PutEndpoint, RequestContext, ResponseOrValue } from './router';
|
|
2
3
|
import { ExpressRouter } from './utils/express.utils';
|
|
3
4
|
/**
|
|
@@ -7,12 +8,19 @@ import { ExpressRouter } from './utils/express.utils';
|
|
|
7
8
|
export declare class RouteTable {
|
|
8
9
|
private readonly app;
|
|
9
10
|
constructor(app: ExpressRouter);
|
|
10
|
-
|
|
11
|
-
get<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
/** Register GET endpoint with full type inference for path/query params. */
|
|
12
|
+
get<Result, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap>(path: string, endpoint: GetEndpoint<Result, PathParams, QueryParams>): this;
|
|
13
|
+
/** Register GET endpoint with function shorthand. */
|
|
14
|
+
get<Result>(path: string, run: (ctx: RequestContext) => ResponseOrValue<Result> | Promise<ResponseOrValue<Result>>): this;
|
|
15
|
+
/** Register POST endpoint with full type inference for path/query params. */
|
|
16
|
+
post<Body, Result = void, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap>(path: string, endpoint: PostEndpoint<Body, Result, PathParams, QueryParams>): this;
|
|
17
|
+
/** Register PATCH endpoint with full type inference for path/query params. */
|
|
18
|
+
patch<Body, Result = void, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap>(path: string, endpoint: PatchEndpoint<Body, Result, PathParams, QueryParams>): this;
|
|
19
|
+
/** Register PUT endpoint with full type inference for path/query params. */
|
|
20
|
+
put<Body, Result = void, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap>(path: string, endpoint: PutEndpoint<Body, Result, PathParams, QueryParams>): this;
|
|
21
|
+
/** Register DELETE endpoint with full endpoint object. */
|
|
22
|
+
delete<PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap>(path: string, endpoint: DeleteEndpoint<PathParams, QueryParams>): this;
|
|
23
|
+
/** Register DELETE endpoint with function shorthand. */
|
|
16
24
|
delete(path: string, run: (ctx: RequestContext) => void | Promise<void>): this;
|
|
17
25
|
}
|
|
18
26
|
/**
|
package/dist/cjs/route-table.js
CHANGED
|
@@ -16,14 +16,17 @@ class RouteTable {
|
|
|
16
16
|
(0, router_1.mountGet)(this.app, path, endpoint);
|
|
17
17
|
return this;
|
|
18
18
|
}
|
|
19
|
+
/** Register POST endpoint with full type inference for path/query params. */
|
|
19
20
|
post(path, endpoint) {
|
|
20
21
|
(0, router_1.mountPost)(this.app, path, endpoint);
|
|
21
22
|
return this;
|
|
22
23
|
}
|
|
24
|
+
/** Register PATCH endpoint with full type inference for path/query params. */
|
|
23
25
|
patch(path, endpoint) {
|
|
24
26
|
(0, router_1.mountPatch)(this.app, path, endpoint);
|
|
25
27
|
return this;
|
|
26
28
|
}
|
|
29
|
+
/** Register PUT endpoint with full type inference for path/query params. */
|
|
27
30
|
put(path, endpoint) {
|
|
28
31
|
(0, router_1.mountPut)(this.app, path, endpoint);
|
|
29
32
|
return this;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-table.js","sourceRoot":"","sources":["../../src/route-table.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"route-table.js","sourceRoot":"","sources":["../../src/route-table.ts"],"names":[],"mappings":";;;AA6GA,4CAEC;AA9GD,qCAakB;AAGlB;;;GAGG;AACH,MAAa,UAAU;IACrB,YAA6B,GAAkB;QAAlB,QAAG,GAAH,GAAG,CAAe;IAAG,CAAC;IAYnD,GAAG,CAKD,IAAY,EACZ,aAEyF;QAEzF,MAAM,QAAQ,GAAG,OAAO,aAAa,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;QAC9F,IAAA,iBAAQ,EAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAAgC,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6EAA6E;IAC7E,IAAI,CAKF,IAAY,EAAE,QAA6D;QAC3E,IAAA,kBAAS,EAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAA4C,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,8EAA8E;IAC9E,KAAK,CAKH,IAAY,EAAE,QAA8D;QAC5E,IAAA,mBAAU,EAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAA6C,CAAC,CAAC;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4EAA4E;IAC5E,GAAG,CAKD,IAAY,EAAE,QAA4D;QAC1E,IAAA,iBAAQ,EAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAA2C,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC;IACd,CAAC;IAWD,MAAM,CAIJ,IAAY,EACZ,aAAwG;QAExG,MAAM,QAAQ,GAAG,OAAO,aAAa,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;QAC9F,IAAA,oBAAW,EAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAA0B,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAjFD,gCAiFC;AAED;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,GAAkB;IACjD,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
|
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,31 +30,31 @@ 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
40
|
/** Handler function. Can be sync or async. */
|
|
49
|
-
run: (ctx:
|
|
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';
|
package/dist/cjs/router.js
CHANGED
|
@@ -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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"}
|