@fishka/express 0.9.16 → 0.9.17

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.
@@ -2,9 +2,9 @@
2
2
  * URL parameter validators for Express path/query params.
3
3
  * All params are strings from Express, so validation starts with string.
4
4
  */
5
- import { TypeValidator } from '../api.types';
5
+ import { ParamValidator } from '../api.types';
6
6
  /** Makes validator optional - returns undefined if value missing */
7
- export declare function optional<T>(validator: TypeValidator<T>): TypeValidator<T | undefined>;
7
+ export declare function optional<T>(validator: ParamValidator<T>): ParamValidator<T | undefined>;
8
8
  /** Operator that transforms string to T */
9
9
  export type ParamOperator<T> = (value: string) => T;
10
10
  /** Operator that transforms T to R */
@@ -14,18 +14,18 @@ export type Operator<T, R = T> = (value: T) => R;
14
14
  * Operators are applied in sequence to validate/transform the value.
15
15
  *
16
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
17
+ * transform() // string
18
+ * transform(toInt) // number
19
+ * transform(toInt, min(1)) // number >= 1
20
+ * transform(minLength(3)) // string with min length
21
+ * transform(trim, lowercase) // trimmed lowercase string
22
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>;
23
+ export declare function transform(): ParamValidator<string>;
24
+ export declare function transform<A>(op1: ParamOperator<A>): ParamValidator<A>;
25
+ export declare function transform<A, B>(op1: ParamOperator<A>, op2: Operator<A, B>): ParamValidator<B>;
26
+ export declare function transform<A, B, C>(op1: ParamOperator<A>, op2: Operator<A, B>, op3: Operator<B, C>): ParamValidator<C>;
27
+ export declare function transform<A, B, C, D>(op1: ParamOperator<A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>): ParamValidator<D>;
28
+ export declare function transform<A, B, C, D, E>(op1: ParamOperator<A>, op2: Operator<A, B>, op3: Operator<B, C>, op4: Operator<C, D>, op5: Operator<D, E>): ParamValidator<E>;
29
29
  /** Parses string to integer */
30
30
  export declare const toInt: (message?: string) => ParamOperator<number>;
31
31
  /** Parses string to number */
@@ -52,7 +52,14 @@ export declare const min: (n: number, message?: string) => Operator<number>;
52
52
  export declare const max: (n: number, message?: string) => Operator<number>;
53
53
  /** Requires value to be in range [minVal, maxVal] */
54
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>;
55
+ /** Adds custom validation with predicate */
56
+ export declare const assert: <T>(predicate: (value: T) => boolean, message: string) => Operator<T>;
57
57
  /** Transforms the value */
58
58
  export declare const map: <T, R>(fn: (value: T) => R) => Operator<T, R>;
59
+ /**
60
+ * Creates a simple validator that returns error message or undefined.
61
+ * Can be used directly in transform().
62
+ * @example
63
+ * transform(validator(s => s === 'valid' ? undefined : 'Invalid ID'))
64
+ */
65
+ export declare function validator<T>(validateFn: (value: T) => string | undefined): (value: T) => T;
@@ -4,9 +4,10 @@
4
4
  * All params are strings from Express, so validation starts with string.
5
5
  */
6
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;
7
+ exports.map = exports.assert = 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
8
  exports.optional = optional;
9
- exports.param = param;
9
+ exports.transform = transform;
10
+ exports.validator = validator;
10
11
  const assertions_1 = require("@fishka/assertions");
11
12
  /** Makes validator optional - returns undefined if value missing */
12
13
  function optional(validator) {
@@ -17,7 +18,7 @@ function optional(validator) {
17
18
  return validator(value);
18
19
  };
19
20
  }
20
- function param(...operators) {
21
+ function transform(...operators) {
21
22
  return (value) => {
22
23
  (0, assertions_1.assertTruthy)(typeof value === 'string', `Expected string, got ${typeof value}`);
23
24
  let result = value;
@@ -27,11 +28,10 @@ function param(...operators) {
27
28
  return result;
28
29
  };
29
30
  }
30
- // ============================================================================
31
31
  // String → T operators (first in chain)
32
- // ============================================================================
33
32
  /** Parses string to integer */
34
33
  const toInt = (message) => (value) => {
34
+ (0, assertions_1.assertTruthy)(value !== undefined && value !== null && value !== '', message ?? 'Expected integer, got undefined or empty');
35
35
  const num = Number(value);
36
36
  (0, assertions_1.assertTruthy)(Number.isInteger(num), message ?? `Expected integer, got '${value}'`);
37
37
  return num;
@@ -39,6 +39,7 @@ const toInt = (message) => (value) => {
39
39
  exports.toInt = toInt;
40
40
  /** Parses string to number */
41
41
  const toNumber = (message) => (value) => {
42
+ (0, assertions_1.assertTruthy)(value !== undefined && value !== null && value !== '', message ?? 'Expected number, got undefined or empty');
42
43
  const num = Number(value);
43
44
  (0, assertions_1.assertTruthy)(!isNaN(num), message ?? `Expected number, got '${value}'`);
44
45
  return num;
@@ -46,6 +47,7 @@ const toNumber = (message) => (value) => {
46
47
  exports.toNumber = toNumber;
47
48
  /** Parses string to boolean ('true'/'false') */
48
49
  const toBool = (message) => (value) => {
50
+ (0, assertions_1.assertTruthy)(value !== undefined && value !== null && value !== '', message ?? 'Expected boolean, got undefined or empty');
49
51
  (0, assertions_1.assertTruthy)(value === 'true' || value === 'false', message ?? `Expected 'true' or 'false', got '${value}'`);
50
52
  return value === 'true';
51
53
  };
@@ -56,9 +58,7 @@ const oneOf = (...allowedValues) => (value) => {
56
58
  return value;
57
59
  };
58
60
  exports.oneOf = oneOf;
59
- // ============================================================================
60
61
  // String operators (string → string)
61
- // ============================================================================
62
62
  /** Requires minimum string length */
63
63
  const minLength = (n, message) => (value) => {
64
64
  (0, assertions_1.assertTruthy)(value.length >= n, message ?? `Must be at least ${n} characters`);
@@ -86,9 +86,7 @@ exports.lowercase = lowercase;
86
86
  /** Converts string to uppercase */
87
87
  const uppercase = (value) => value.toUpperCase();
88
88
  exports.uppercase = uppercase;
89
- // ============================================================================
90
89
  // Number operators (number → number)
91
- // ============================================================================
92
90
  /** Requires minimum value */
93
91
  const min = (n, message) => (value) => {
94
92
  (0, assertions_1.assertTruthy)(value >= n, message ?? `Must be at least ${n}`);
@@ -107,16 +105,29 @@ const range = (minVal, maxVal, message) => (value) => {
107
105
  return value;
108
106
  };
109
107
  exports.range = range;
110
- // ============================================================================
111
108
  // Generic operators
112
- // ============================================================================
113
- /** Adds custom validation */
114
- const check = (predicate, message) => (value) => {
109
+ /** Adds custom validation with predicate */
110
+ const assert = (predicate, message) => (value) => {
115
111
  (0, assertions_1.assertTruthy)(predicate(value), message);
116
112
  return value;
117
113
  };
118
- exports.check = check;
114
+ exports.assert = assert;
119
115
  /** Transforms the value */
120
116
  const map = (fn) => (value) => fn(value);
121
117
  exports.map = map;
118
+ /**
119
+ * Creates a simple validator that returns error message or undefined.
120
+ * Can be used directly in transform().
121
+ * @example
122
+ * transform(validator(s => s === 'valid' ? undefined : 'Invalid ID'))
123
+ */
124
+ function validator(validateFn) {
125
+ return (value) => {
126
+ const error = validateFn(value);
127
+ if (error !== undefined) {
128
+ (0, assertions_1.assertTruthy)(false, error);
129
+ }
130
+ return value;
131
+ };
132
+ }
122
133
  //# sourceMappingURL=type-validators.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"type-validators.js","sourceRoot":"","sources":["../../../src/utils/type-validators.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"type-validators.js","sourceRoot":"","sources":["../../../src/utils/type-validators.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,11 @@
1
- /** Validator function that validates and returns typed value */
2
- export type TypeValidator<T> = (value: unknown) => T;
1
+ /** Validator function that validates and returns a typed value */
2
+ export type ParamValidator<T> = (value: unknown) => T;
3
3
  /** Map of param name to type validator */
4
- export type TypedValidatorMap = Record<string, TypeValidator<unknown>>;
4
+ export type ParamValidatorMap = Record<string, ParamValidator<unknown>>;
5
5
  /** Infer validated types from validator map */
6
- export type InferValidated<T extends TypedValidatorMap | undefined> = T extends TypedValidatorMap ? {
6
+ export type ValidatedParams<T extends ParamValidatorMap | undefined> = T extends ParamValidatorMap ? {
7
7
  [K in keyof T]: ReturnType<T[K]>;
8
- } : Record<string, string>;
8
+ } : Record<string, never>;
9
9
  export declare class HttpError extends Error {
10
10
  readonly status: number;
11
11
  readonly details?: Record<string, unknown> | undefined;
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"api.types.js","sourceRoot":"","sources":["../../src/api.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAalD,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"}
@@ -1,4 +1,3 @@
1
- import { TypedValidatorMap } from './api.types';
2
1
  import { DeleteEndpoint, GetEndpoint, PatchEndpoint, PostEndpoint, PutEndpoint, RequestContext, ResponseOrValue } from './router';
3
2
  import { ExpressRouter } from './utils/express.utils';
4
3
  /**
@@ -8,19 +7,25 @@ import { ExpressRouter } from './utils/express.utils';
8
7
  export declare class RouteTable {
9
8
  private readonly app;
10
9
  constructor(app: ExpressRouter);
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. */
10
+ /** Register a GET endpoint. */
11
+ get<Result>(path: string, endpoint: GetEndpoint<Result>): this;
12
+ /** Register a GET endpoint with function shorthand. */
14
13
  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. */
14
+ /** Register a POST endpoint. */
15
+ post<Result = void>(path: string, endpoint: PostEndpoint<Result>): this;
16
+ /** Register a POST endpoint with function shorthand. */
17
+ post<Result = void>(path: string, run: (ctx: RequestContext) => ResponseOrValue<Result> | Promise<ResponseOrValue<Result>>): this;
18
+ /** Register a PATCH endpoint. */
19
+ patch<Result = void>(path: string, endpoint: PatchEndpoint<Result>): this;
20
+ /** Register a PATCH endpoint with function shorthand. */
21
+ patch<Result = void>(path: string, run: (ctx: RequestContext) => ResponseOrValue<Result> | Promise<ResponseOrValue<Result>>): this;
22
+ /** Register a PUT endpoint. */
23
+ put<Result = void>(path: string, endpoint: PutEndpoint<Result>): this;
24
+ /** Register a PUT endpoint with function shorthand. */
25
+ put<Result = void>(path: string, run: (ctx: RequestContext) => ResponseOrValue<Result> | Promise<ResponseOrValue<Result>>): this;
26
+ /** Register a DELETE endpoint with a full endpoint object. */
27
+ delete(path: string, endpoint: DeleteEndpoint): this;
28
+ /** Register a DELETE endpoint with function shorthand. */
24
29
  delete(path: string, run: (ctx: RequestContext) => void | Promise<void>): this;
25
30
  }
26
31
  /**
@@ -12,18 +12,18 @@ export class RouteTable {
12
12
  mountGet(this.app, path, endpoint);
13
13
  return this;
14
14
  }
15
- /** Register POST endpoint with full type inference for path/query params. */
16
- post(path, endpoint) {
15
+ post(path, endpointOrRun) {
16
+ const endpoint = typeof endpointOrRun === 'function' ? { run: endpointOrRun } : endpointOrRun;
17
17
  mountPost(this.app, path, endpoint);
18
18
  return this;
19
19
  }
20
- /** Register PATCH endpoint with full type inference for path/query params. */
21
- patch(path, endpoint) {
20
+ patch(path, endpointOrRun) {
21
+ const endpoint = typeof endpointOrRun === 'function' ? { run: endpointOrRun } : endpointOrRun;
22
22
  mountPatch(this.app, path, endpoint);
23
23
  return this;
24
24
  }
25
- /** Register PUT endpoint with full type inference for path/query params. */
26
- put(path, endpoint) {
25
+ put(path, endpointOrRun) {
26
+ const endpoint = typeof endpointOrRun === 'function' ? { run: endpointOrRun } : endpointOrRun;
27
27
  mountPut(this.app, path, endpoint);
28
28
  return this;
29
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"route-table.js","sourceRoot":"","sources":["../../src/route-table.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,WAAW,EACX,QAAQ,EACR,UAAU,EACV,SAAS,EACT,QAAQ,GAMT,MAAM,UAAU,CAAC;AAGlB;;;GAGG;AACH,MAAM,OAAO,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,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAAgC,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6EAA6E;IAC7E,IAAI,CAKF,IAAY,EAAE,QAA6D;QAC3E,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAA4C,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,8EAA8E;IAC9E,KAAK,CAKH,IAAY,EAAE,QAA8D;QAC5E,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAA6C,CAAC,CAAC;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4EAA4E;IAC5E,GAAG,CAKD,IAAY,EAAE,QAA4D;QAC1E,QAAQ,CAAC,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,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAA0B,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAkB;IACjD,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"route-table.js","sourceRoot":"","sources":["../../src/route-table.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,WAAW,EACX,QAAQ,EACR,UAAU,EACV,SAAS,EACT,QAAQ,GAMT,MAAM,UAAU,CAAC;AAGlB;;;GAGG;AACH,MAAM,OAAO,UAAU;IACrB,YAA6B,GAAkB;QAAlB,QAAG,GAAH,GAAG,CAAe;IAAG,CAAC;IAQnD,GAAG,CACD,IAAY,EACZ,aAA0H;QAE1H,MAAM,QAAQ,GAAG,OAAO,aAAa,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;QAC9F,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAAgC,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAQD,IAAI,CACF,IAAY,EACZ,aAA2H;QAE3H,MAAM,QAAQ,GAAG,OAAO,aAAa,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;QAC9F,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAAiC,CAAC,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;IAQD,KAAK,CACH,IAAY,EACZ,aAA4H;QAE5H,MAAM,QAAQ,GAAG,OAAO,aAAa,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;QAC9F,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAAkC,CAAC,CAAC;QAC/D,OAAO,IAAI,CAAC;IACd,CAAC;IAQD,GAAG,CACD,IAAY,EACZ,aAA0H;QAE1H,MAAM,QAAQ,GAAG,OAAO,aAAa,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;QAC9F,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAAgC,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAQD,MAAM,CACJ,IAAY,EACZ,aAA+E;QAE/E,MAAM,QAAQ,GAAG,OAAO,aAAa,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;QAC9F,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAA0B,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAkB;IACjD,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
@@ -1,5 +1,5 @@
1
- import { Assertion, ObjectAssertion } from '@fishka/assertions';
2
- import { ApiResponse, InferValidated, TypedValidatorMap } from './api.types';
1
+ import { Assertion } from '@fishka/assertions';
2
+ import { ApiResponse, ParamValidator } 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,19 +10,36 @@ 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, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> {
14
- /** Parsed and validated request body (for POST/PATCH/PUT handlers). */
15
- body: Body;
13
+ export interface RequestContext {
16
14
  /** Express Request object. */
17
15
  req: ExpressRequest;
18
16
  /** Express Response object. */
19
17
  res: ExpressResponse;
20
18
  /** Authenticated user (if any). Populated by auth middleware. */
21
19
  authUser?: AuthUser;
22
- /** Validated path parameters (typed from $path validators). */
23
- path: InferValidated<PathParams>;
24
- /** Validated query parameters (typed from $query validators). */
25
- query: InferValidated<QueryParams>;
20
+ /**
21
+ * Get and validate a path parameter.
22
+ * @param name - Name of the path parameter
23
+ * @param validator - Optional validator. If not provided, returns the raw string value.
24
+ * @returns Validated value of type T (or string if no validator)
25
+ * @throws {HttpError} 400 Bad Request if validation fails
26
+ */
27
+ path<T = string>(name: string, validator?: ParamValidator<T>): T;
28
+ /**
29
+ * Get and validate a query parameter.
30
+ * @param name - Name of the query parameter
31
+ * @param validator - Optional validator. If not provided, returns the raw string value or undefined.
32
+ * @returns Validated value of type T, or undefined if parameter is not present
33
+ * @throws {HttpError} 400 Bad Request if validation fails
34
+ */
35
+ query<T = string>(name: string, validator?: ParamValidator<T>): T | undefined;
36
+ /**
37
+ * Get and validate the request body.
38
+ * @param validator - Validator function or object assertion
39
+ * @returns Validated body of type T
40
+ * @throws {HttpError} 400 Bad Request if validation fails
41
+ */
42
+ body<T>(validator: Assertion<T>): T;
26
43
  /**
27
44
  * Generic state storage for middleware to attach data.
28
45
  * Allows middleware to pass information to handlers and other middleware.
@@ -30,31 +47,24 @@ export interface RequestContext<Body = void, PathParams extends TypedValidatorMa
30
47
  state: Map<string, unknown>;
31
48
  }
32
49
  /** Base interface with common endpoint properties. */
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;
50
+ export interface EndpointBase<Result = unknown> {
38
51
  /** Optional middleware to execute before the handler. */
39
52
  middlewares?: Array<EndpointMiddleware>;
40
53
  /** Handler function. Can be sync or async. */
41
- run: (ctx: RequestContext<Body, PathParams, QueryParams>) => ResponseOrValue<Result> | Promise<ResponseOrValue<Result>>;
54
+ run: (ctx: RequestContext) => ResponseOrValue<Result> | Promise<ResponseOrValue<Result>>;
42
55
  }
43
56
  /** Descriptor for GET list routes. */
44
- export type GetListEndpoint<ResultElementType, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> = EndpointBase<PathParams, QueryParams, void, Array<ResultElementType>>;
57
+ export type GetListEndpoint<ResultElementType> = EndpointBase<Array<ResultElementType>>;
45
58
  /** Descriptor for GET routes. */
46
- export type GetEndpoint<Result, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> = EndpointBase<PathParams, QueryParams, void, Result>;
59
+ export type GetEndpoint<Result> = EndpointBase<Result>;
47
60
  /** Descriptor for POST routes. */
48
- export interface PostEndpoint<Body, Result = void, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> extends EndpointBase<PathParams, QueryParams, Body, Result> {
49
- /** Request body validator. */
50
- $body: Body extends object ? ObjectAssertion<Body> : Assertion<Body>;
51
- }
61
+ export type PostEndpoint<Result = void> = EndpointBase<Result>;
52
62
  /** Same as POST. Used for full object updates. */
53
- export type PutEndpoint<Body, Result = void, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> = PostEndpoint<Body, Result, PathParams, QueryParams>;
63
+ export type PutEndpoint<Result = void> = EndpointBase<Result>;
54
64
  /** Same as PUT. While PUT is used for the whole object update, PATCH is used for a partial update. */
55
- export type PatchEndpoint<Body, Result = void, PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> = PutEndpoint<Body, Result, PathParams, QueryParams>;
65
+ export type PatchEndpoint<Result = void> = EndpointBase<Result>;
56
66
  /** Descriptor for DELETE routes. */
57
- export type DeleteEndpoint<PathParams extends TypedValidatorMap = TypedValidatorMap, QueryParams extends TypedValidatorMap = TypedValidatorMap> = EndpointBase<PathParams, QueryParams, void, void>;
67
+ export type DeleteEndpoint = EndpointBase<void>;
58
68
  /** Union type for all route registration info objects. */
59
69
  export type RouteRegistrationInfo = ({
60
70
  method: 'get';
@@ -77,11 +87,11 @@ export type RouteRegistrationInfo = ({
77
87
  /** Registers a GET route. */
78
88
  export declare const mountGet: (app: ExpressRouter, path: string, endpoint: GetEndpoint<unknown> | GetListEndpoint<unknown>) => void;
79
89
  /** Registers a POST route. */
80
- export declare const mountPost: <Body, Result>(app: ExpressRouter, path: string, endpoint: PostEndpoint<Body, Result>) => void;
90
+ export declare const mountPost: <Result>(app: ExpressRouter, path: string, endpoint: PostEndpoint<Result>) => void;
81
91
  /** Registers a PATCH route. */
82
- export declare const mountPatch: <Body, Result>(app: ExpressRouter, path: string, endpoint: PatchEndpoint<Body, Result>) => void;
92
+ export declare const mountPatch: <Result>(app: ExpressRouter, path: string, endpoint: PatchEndpoint<Result>) => void;
83
93
  /** Registers a PUT route. */
84
- export declare const mountPut: <Body, Result>(app: ExpressRouter, path: string, endpoint: PutEndpoint<Body, Result>) => void;
94
+ export declare const mountPut: <Result>(app: ExpressRouter, path: string, endpoint: PutEndpoint<Result>) => void;
85
95
  /** Registers a DELETE route. */
86
96
  export declare const mountDelete: (app: ExpressRouter, path: string, endpoint: DeleteEndpoint) => void;
87
97
  /** Mounts a route with the given method, endpoint, and path. */
@@ -5,9 +5,94 @@ import { catchRouteErrors } from './error-handling';
5
5
  import { HTTP_BAD_REQUEST, HTTP_OK } from './http-status-codes';
6
6
  import { getRequestLocalStorage } from './thread-local/thread-local-storage';
7
7
  import { wrapAsApiResponse } from './utils/conversion.utils';
8
- // ============================================================================
9
- // Internal implementation details
10
- // ============================================================================
8
+ /** Implementation of RequestContext with caching for validated parameters. */
9
+ class RequestContextImpl {
10
+ constructor(
11
+ /** Express request object. */
12
+ req,
13
+ /** Express response object. */
14
+ res,
15
+ /** Authenticated user (if any). */
16
+ authUser,
17
+ /** Request-scoped state storage. */
18
+ state = new Map()) {
19
+ this.req = req;
20
+ this.res = res;
21
+ this.authUser = authUser;
22
+ this.state = state;
23
+ }
24
+ /**
25
+ * Validates a parameter with optional validator and caching.
26
+ * @param name Parameter name.
27
+ * @param rawValue Raw parameter value from request.
28
+ * @param validator Optional validator function.
29
+ * @param cache Cache map for this parameter type.
30
+ * @param isRequired Whether parameter is required (path=true, query=false).
31
+ * @returns Validated value or undefined for optional missing parameters.
32
+ */
33
+ validateParam(name, rawValue, validator, isRequired) {
34
+ try {
35
+ let result;
36
+ if (validator) {
37
+ // Pass value to validator even if it's undefined/null/empty
38
+ result = validator(rawValue);
39
+ }
40
+ else {
41
+ // Without validator
42
+ if (rawValue === undefined || rawValue === null || rawValue === '') {
43
+ if (isRequired) {
44
+ assertHttp(false, HTTP_BAD_REQUEST, `Missing required parameter: ${name}`);
45
+ }
46
+ return undefined;
47
+ }
48
+ result = rawValue;
49
+ }
50
+ return result;
51
+ }
52
+ catch (error) {
53
+ if (error instanceof HttpError)
54
+ throw error;
55
+ throw new HttpError(HTTP_BAD_REQUEST, getMessageFromError(error));
56
+ }
57
+ }
58
+ path(name, validator) {
59
+ const rawValue = this.req.params[name];
60
+ const result = this.validateParam(name, rawValue, validator, true);
61
+ assertHttp(result !== undefined, HTTP_BAD_REQUEST, `Missing required path parameter: ${name}`);
62
+ return result;
63
+ }
64
+ query(name, validator) {
65
+ const parsedUrl = url.parse(this.req.url, true);
66
+ const rawValue = parsedUrl.query[name];
67
+ const value = Array.isArray(rawValue) ? rawValue[0] : rawValue;
68
+ return this.validateParam(name, value, validator, false);
69
+ }
70
+ body(validator) {
71
+ const apiRequest = this.req.body;
72
+ try {
73
+ // Handle validation based on whether the validator is an object or function
74
+ if (typeof validator === 'function') {
75
+ // It's a ValueAssertion (function) - call it directly
76
+ validator(apiRequest);
77
+ }
78
+ else {
79
+ // It's an ObjectAssertion - use validateObject
80
+ const objectValidator = validator;
81
+ const isEmptyValidator = Object.keys(objectValidator).length === 0;
82
+ const errorMessage = validateObject(apiRequest, objectValidator, `${HTTP_BAD_REQUEST}: request body`, {
83
+ failOnUnknownFields: !isEmptyValidator,
84
+ });
85
+ assertHttp(!errorMessage, HTTP_BAD_REQUEST, errorMessage || 'Request body validation failed');
86
+ }
87
+ return apiRequest;
88
+ }
89
+ catch (error) {
90
+ if (error instanceof HttpError)
91
+ throw error;
92
+ throw new HttpError(HTTP_BAD_REQUEST, getMessageFromError(error));
93
+ }
94
+ }
95
+ }
11
96
  /** Registers a GET route. */
12
97
  export const mountGet = (app, path, endpoint) => mount(app, { method: 'get', endpoint, path });
13
98
  /** Registers a POST route. */
@@ -54,48 +139,12 @@ function createRouteHandler(method, endpoint) {
54
139
  res.send(response);
55
140
  };
56
141
  }
57
- /**
58
- * @Internal
59
- * Validates and builds typed path/query parameters using $path and $query validators.
60
- */
61
- function buildValidatedParams(req, $path, $query) {
62
- const pathResult = {};
63
- const queryResult = {};
64
- try {
65
- // Validate path params
66
- if ($path) {
67
- for (const [key, validator] of Object.entries($path)) {
68
- const value = req.params[key];
69
- pathResult[key] = validator(value);
70
- }
71
- }
72
- // Validate query params
73
- if ($query) {
74
- const parsedUrl = url.parse(req.url, true);
75
- for (const [key, validator] of Object.entries($query)) {
76
- const rawValue = parsedUrl.query[key];
77
- const value = Array.isArray(rawValue) ? rawValue[0] : rawValue;
78
- queryResult[key] = validator(value);
79
- }
80
- }
81
- }
82
- catch (error) {
83
- if (error instanceof HttpError)
84
- throw error;
85
- throw new HttpError(HTTP_BAD_REQUEST, getMessageFromError(error));
86
- }
87
- return {
88
- path: pathResult,
89
- query: queryResult,
90
- };
91
- }
92
142
  /**
93
143
  * @Internal
94
144
  * Runs GET handler with optional middleware.
95
145
  */
96
146
  async function executeGetEndpoint(route, req, res) {
97
- const validated = buildValidatedParams(req, route.$path, route.$query);
98
- const requestContext = newRequestContext(undefined, req, res, validated.path, validated.query);
147
+ const requestContext = new RequestContextImpl(req, res);
99
148
  return await executeWithMiddleware(() => route.run(requestContext), route.middlewares || [], requestContext);
100
149
  }
101
150
  /**
@@ -103,8 +152,7 @@ async function executeGetEndpoint(route, req, res) {
103
152
  * Runs DELETE handler with optional middleware.
104
153
  */
105
154
  async function executeDeleteEndpoint(route, req, res) {
106
- const validated = buildValidatedParams(req, route.$path, route.$query);
107
- const requestContext = newRequestContext(undefined, req, res, validated.path, validated.query);
155
+ const requestContext = new RequestContextImpl(req, res);
108
156
  await executeWithMiddleware(() => route.run(requestContext), route.middlewares || [], requestContext);
109
157
  return undefined;
110
158
  }
@@ -113,32 +161,8 @@ async function executeDeleteEndpoint(route, req, res) {
113
161
  * Runs POST/PUT/PATCH handler with optional middleware.
114
162
  */
115
163
  async function executeBodyEndpoint(route, req, res) {
116
- const validator = route.$body;
117
- const apiRequest = req.body;
118
- try {
119
- // Handle validation based on whether the validator is an object or function
120
- if (typeof validator === 'function') {
121
- // It's a ValueAssertion (function) - call it directly
122
- validator(apiRequest);
123
- }
124
- else {
125
- // It's an ObjectAssertion - use validateObject
126
- const objectValidator = validator;
127
- const isEmptyValidator = Object.keys(objectValidator).length === 0;
128
- const errorMessage = validateObject(apiRequest, objectValidator, `${HTTP_BAD_REQUEST}: request body`, {
129
- failOnUnknownFields: !isEmptyValidator,
130
- });
131
- assertHttp(!errorMessage, HTTP_BAD_REQUEST, errorMessage || 'Request body validation failed');
132
- }
133
- }
134
- catch (error) {
135
- if (error instanceof HttpError)
136
- throw error;
137
- throw new HttpError(HTTP_BAD_REQUEST, getMessageFromError(error));
138
- }
139
- const validated = buildValidatedParams(req, route.$path, route.$query);
140
- const requestContext = newRequestContext(apiRequest, req, res, validated.path, validated.query);
141
- return await executeWithMiddleware(() => route.run(requestContext), (route.middlewares || []), requestContext);
164
+ const requestContext = new RequestContextImpl(req, res);
165
+ return await executeWithMiddleware(() => route.run(requestContext), route.middlewares || [], requestContext);
142
166
  }
143
167
  /**
144
168
  * @Internal
@@ -155,18 +179,4 @@ async function executeWithMiddleware(run, middlewares, context) {
155
179
  };
156
180
  return await current(0);
157
181
  }
158
- /**
159
- * @Internal
160
- * Creates a new RequestContext instance.
161
- */
162
- function newRequestContext(requestBody, req, res, validatedPath, validatedQuery) {
163
- return {
164
- body: requestBody,
165
- req,
166
- res,
167
- path: validatedPath,
168
- query: validatedQuery,
169
- state: new Map(),
170
- };
171
- }
172
182
  //# sourceMappingURL=router.js.map