@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.
Files changed (38) hide show
  1. package/README.md +108 -38
  2. package/dist/cjs/api.types.d.ts +8 -21
  3. package/dist/cjs/api.types.js +1 -21
  4. package/dist/cjs/api.types.js.map +1 -1
  5. package/dist/cjs/error-handling.d.ts +72 -3
  6. package/dist/cjs/error-handling.js +107 -7
  7. package/dist/cjs/error-handling.js.map +1 -1
  8. package/dist/cjs/index.d.ts +1 -0
  9. package/dist/cjs/index.js +1 -0
  10. package/dist/cjs/index.js.map +1 -1
  11. package/dist/cjs/route-table.d.ts +15 -7
  12. package/dist/cjs/route-table.js +3 -0
  13. package/dist/cjs/route-table.js.map +1 -1
  14. package/dist/cjs/router.d.ts +25 -33
  15. package/dist/cjs/router.js +40 -60
  16. package/dist/cjs/router.js.map +1 -1
  17. package/dist/cjs/utils/type-validators.d.ts +58 -0
  18. package/dist/cjs/utils/type-validators.js +122 -0
  19. package/dist/cjs/utils/type-validators.js.map +1 -0
  20. package/dist/esm/api.types.d.ts +8 -21
  21. package/dist/esm/api.types.js +0 -18
  22. package/dist/esm/api.types.js.map +1 -1
  23. package/dist/esm/error-handling.d.ts +72 -3
  24. package/dist/esm/error-handling.js +106 -7
  25. package/dist/esm/error-handling.js.map +1 -1
  26. package/dist/esm/index.d.ts +1 -0
  27. package/dist/esm/index.js +1 -0
  28. package/dist/esm/index.js.map +1 -1
  29. package/dist/esm/route-table.d.ts +15 -7
  30. package/dist/esm/route-table.js +3 -0
  31. package/dist/esm/route-table.js.map +1 -1
  32. package/dist/esm/router.d.ts +25 -33
  33. package/dist/esm/router.js +42 -62
  34. package/dist/esm/router.js.map +1 -1
  35. package/dist/esm/utils/type-validators.d.ts +58 -0
  36. package/dist/esm/utils/type-validators.js +102 -0
  37. package/dist/esm/utils/type-validators.js.map +1 -0
  38. 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 - using function shorthand
23
- routes.get<{ id: string; name: string }>('users/:id', async ctx => ({
24
- id: ctx.params.get('id'),
25
- name: 'John',
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 - using full endpoint object
29
- routes.get<Array<{ id: string; name: string }>>('users', async () => [
30
- { id: '1', name: 'John' },
31
- { id: '2', name: 'Jane' },
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: string }>('users', {
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: '1' }),
41
+ run: async ctx => ({ id: 1 }),
38
42
  });
39
43
 
40
- // DELETE /users/:id - using function shorthand
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 Parameters
50
+ ## URL Parameter Validation
49
51
 
50
- Global validation can be enforced for specific URL parameters (e.g., `:id`, `:orgId`) across all routes.
52
+ Use `param()` to validate and transform path/query parameters. All operators are composable:
51
53
 
52
54
  ```typescript
53
- import { registerUrlParameter } from '@fishka/express';
54
- import { assertString, assertTruthy } from '@fishka/assertions';
55
-
56
- // Register parameters with optional validation
57
- registerUrlParameter('orgId', {
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
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
61
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
63
82
 
64
- // Now /orgs/:orgId will automatically validate that orgId starts with 'org-'
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
100
+
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
- Here is a full initialization including TLS context, global validation, and proper error handling.
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, registerUrlParameter } from '@fishka/express';
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,21 +173,43 @@ app.use(express.json());
125
173
  // 2. Initialize TLS context (Request IDs, etc.)
126
174
  app.use(createTlsMiddleware());
127
175
 
128
- // 3. Register global URL parameters
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
- // 5. Global error handler (Must be after route definitions)
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
138
187
  app.use(catchAllMiddleware);
139
188
 
140
189
  app.listen(3000);
141
190
  ```
142
191
 
192
+ ## Process Handlers
193
+
194
+ Handle uncaught errors and graceful shutdown in one place:
195
+
196
+ ```typescript
197
+ import { installProcessHandlers } from '@fishka/express';
198
+
199
+ installProcessHandlers({
200
+ // Error handlers
201
+ onUncaughtException: err => sendToMonitoring(err),
202
+ onUnhandledRejection: reason => sendToMonitoring(reason),
203
+
204
+ // Graceful shutdown
205
+ onShutdown: async () => {
206
+ await database.close();
207
+ await server.close();
208
+ },
209
+ shutdownTimeout: 15000, // Force exit after 15s (default: 10s)
210
+ });
211
+ ```
212
+
143
213
  ## License
144
214
 
145
215
  MIT
@@ -1,5 +1,11 @@
1
- import { ValueAssertion } from '@fishka/assertions';
2
- export type UrlTokensValidator = Record<string, ValueAssertion<string>>;
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;
@@ -1,12 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.URL_PARAMETER_INFO = exports.HttpError = void 0;
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":";;;AAgDA,gCAEC;AA2BD,4BAEC;AAiBD,oDAEC;AAMD,gDAOC;AA/GD,mDAAkE;AAClE,2DAAuD;AAIvD,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;AAUD;;;GAGG;AACU,QAAA,kBAAkB,GAAqC,EAAE,CAAC;AAEvE,qCAAqC;AACrC,SAAgB,oBAAoB,CAAC,IAAY,EAAE,IAAsB;IACvE,0BAAkB,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,SAAgB,kBAAkB,CAAC,IAAa;IAC9C,UAAU,CAAC,OAAO,IAAI,KAAK,QAAQ,EAAE,oCAAgB,EAAE,qCAAqC,CAAC,CAAC;IAC9F,UAAU,CACR,0BAAkB,CAAC,IAAI,CAAC,EACxB,oCAAgB,EAChB,2BAA2B,IAAI,sDAAsD,IAAI,UAAU,CACpG,CAAC;AACJ,CAAC"}
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"}
@@ -1,9 +1,78 @@
1
1
  import { NextFunction } from 'express';
2
2
  import { ExpressFunction, ExpressRequest, ExpressResponse } from './utils/express.utils';
3
- /** Catches all kinds of unprocessed exceptions thrown from a single route. */
3
+ /**
4
+ * @Internal
5
+ * Wraps a route handler to catch and convert errors to API responses.
6
+ * Applied automatically to all routes registered via createRouteTable().
7
+ *
8
+ * Catches:
9
+ * - Errors thrown in validators ($path, $query, $body)
10
+ * - Errors thrown in the run() handler
11
+ * - Errors thrown in endpoint middlewares
12
+ *
13
+ * Logs errors to console (error level for 5xx).
14
+ */
4
15
  export declare function catchRouteErrors(fn: ExpressFunction): ExpressFunction;
5
16
  /**
6
- * Catches all errors in Express.js and is installed as global middleware.
7
- * Note that individual routes are wrapped with 'catchRouteErrors' middleware.
17
+ * Express error-handling middleware (4 parameters) for catching errors.
18
+ * Can be mounted at any level - global, path-specific, or router-specific.
19
+ *
20
+ * @example
21
+ * // Global
22
+ * app.use(catchAllMiddleware);
23
+ *
24
+ * // Path-specific
25
+ * app.use('/api', catchAllMiddleware);
26
+ *
27
+ * // Router-specific
28
+ * const router = express.Router();
29
+ * router.use(catchAllMiddleware);
30
+ *
31
+ * Catches:
32
+ * - Errors from Express middleware (passed via next(error))
33
+ * - JSON parsing errors (SyntaxError) - returns 400
34
+ * - Any errors that escape catchRouteErrors
35
+ *
36
+ * Note: Individual routes are already wrapped with catchRouteErrors(),
37
+ * so this middleware primarily catches middleware and parsing errors.
8
38
  */
9
39
  export declare function catchAllMiddleware(error: unknown, _: ExpressRequest, res: ExpressResponse, next: NextFunction): Promise<void>;
40
+ /** Options for installProcessHandlers(). */
41
+ export interface ProcessHandlersOptions {
42
+ /** Custom handler for uncaught exceptions. Called before default logging. */
43
+ onUncaughtException?: (error: Error) => void;
44
+ /** Custom handler for unhandled promise rejections. Called before default logging. */
45
+ onUnhandledRejection?: (reason: unknown) => void;
46
+ /** Async cleanup function called on shutdown signals. */
47
+ onShutdown?: () => Promise<void>;
48
+ /** Force exit timeout in ms if shutdown hangs. Default: 10000 */
49
+ shutdownTimeout?: number;
50
+ /** Signals to handle for graceful shutdown. Default: ['SIGTERM', 'SIGINT'] */
51
+ shutdownSignals?: NodeJS.Signals[];
52
+ }
53
+ /**
54
+ * Installs process-level handlers for errors and graceful shutdown.
55
+ * Call once at application startup, before app.listen().
56
+ *
57
+ * Error handling:
58
+ * - Uncaught exceptions (sync throws outside Express middleware)
59
+ * - Unhandled promise rejections (forgotten await, missing .catch())
60
+ *
61
+ * Graceful shutdown:
62
+ * - SIGTERM (Docker/K8s/systemd stop)
63
+ * - SIGINT (Ctrl+C)
64
+ * - Timeout protection (force exit if shutdown hangs)
65
+ * - Double-shutdown prevention
66
+ *
67
+ * @example
68
+ * installProcessHandlers({
69
+ * onUncaughtException: (error) => sendToMonitoring(error),
70
+ * onUnhandledRejection: (reason) => sendToMonitoring(reason),
71
+ * onShutdown: async () => {
72
+ * await database.close();
73
+ * await server.close();
74
+ * },
75
+ * shutdownTimeout: 15000,
76
+ * });
77
+ */
78
+ export declare function installProcessHandlers(options?: ProcessHandlersOptions): void;
@@ -2,11 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.catchRouteErrors = catchRouteErrors;
4
4
  exports.catchAllMiddleware = catchAllMiddleware;
5
+ exports.installProcessHandlers = installProcessHandlers;
5
6
  const assertions_1 = require("@fishka/assertions");
6
7
  const api_types_1 = require("./api.types");
7
8
  const http_status_codes_1 = require("./http-status-codes");
8
9
  const thread_local_storage_1 = require("./thread-local/thread-local-storage");
9
10
  const conversion_utils_1 = require("./utils/conversion.utils");
11
+ /**
12
+ * Converts any error into a standardized API response format.
13
+ * - HttpError: Uses the error's status code and message
14
+ * - Other errors: Returns 500 with the error message or 'Internal error'
15
+ * Attaches requestId from thread-local storage if available.
16
+ */
10
17
  function buildApiResponse(error) {
11
18
  const tls = (0, thread_local_storage_1.getRequestLocalStorage)();
12
19
  const requestId = tls?.requestId;
@@ -32,7 +39,18 @@ function buildApiResponse(error) {
32
39
  }
33
40
  return response;
34
41
  }
35
- /** Catches all kinds of unprocessed exceptions thrown from a single route. */
42
+ /**
43
+ * @Internal
44
+ * Wraps a route handler to catch and convert errors to API responses.
45
+ * Applied automatically to all routes registered via createRouteTable().
46
+ *
47
+ * Catches:
48
+ * - Errors thrown in validators ($path, $query, $body)
49
+ * - Errors thrown in the run() handler
50
+ * - Errors thrown in endpoint middlewares
51
+ *
52
+ * Logs errors to console (error level for 5xx).
53
+ */
36
54
  function catchRouteErrors(fn) {
37
55
  return async (req, res, next) => {
38
56
  try {
@@ -43,17 +61,33 @@ function catchRouteErrors(fn) {
43
61
  if (apiResponse.status >= http_status_codes_1.HTTP_INTERNAL_SERVER_ERROR) {
44
62
  console.error(`catchRouteErrors: ${req.path}`, error);
45
63
  }
46
- else {
47
- console.log(`catchRouteErrors: ${req.path}`, error);
48
- }
49
64
  res.status(apiResponse.status);
50
65
  res.send(apiResponse);
51
66
  }
52
67
  };
53
68
  }
54
69
  /**
55
- * Catches all errors in Express.js and is installed as global middleware.
56
- * Note that individual routes are wrapped with 'catchRouteErrors' middleware.
70
+ * Express error-handling middleware (4 parameters) for catching errors.
71
+ * Can be mounted at any level - global, path-specific, or router-specific.
72
+ *
73
+ * @example
74
+ * // Global
75
+ * app.use(catchAllMiddleware);
76
+ *
77
+ * // Path-specific
78
+ * app.use('/api', catchAllMiddleware);
79
+ *
80
+ * // Router-specific
81
+ * const router = express.Router();
82
+ * router.use(catchAllMiddleware);
83
+ *
84
+ * Catches:
85
+ * - Errors from Express middleware (passed via next(error))
86
+ * - JSON parsing errors (SyntaxError) - returns 400
87
+ * - Any errors that escape catchRouteErrors
88
+ *
89
+ * Note: Individual routes are already wrapped with catchRouteErrors(),
90
+ * so this middleware primarily catches middleware and parsing errors.
57
91
  */
58
92
  async function catchAllMiddleware(error, _, res, next) {
59
93
  if (!error) {
@@ -63,9 +97,75 @@ async function catchAllMiddleware(error, _, res, next) {
63
97
  // Report as critical. This kind of error should never happen.
64
98
  console.error('catchAllMiddleware:', (0, assertions_1.getMessageFromError)(error));
65
99
  const apiResponse = error instanceof SyntaxError // JSON body parsing error.
66
- ? buildApiResponse(`${http_status_codes_1.HTTP_BAD_REQUEST}: Failed to parse request: ${error.message}`)
100
+ ? buildApiResponse(new api_types_1.HttpError(http_status_codes_1.HTTP_BAD_REQUEST, `Failed to parse request: ${error.message}`))
67
101
  : buildApiResponse(error);
68
102
  res.status(apiResponse.status);
69
103
  res.send(apiResponse);
70
104
  }
105
+ /**
106
+ * Installs process-level handlers for errors and graceful shutdown.
107
+ * Call once at application startup, before app.listen().
108
+ *
109
+ * Error handling:
110
+ * - Uncaught exceptions (sync throws outside Express middleware)
111
+ * - Unhandled promise rejections (forgotten await, missing .catch())
112
+ *
113
+ * Graceful shutdown:
114
+ * - SIGTERM (Docker/K8s/systemd stop)
115
+ * - SIGINT (Ctrl+C)
116
+ * - Timeout protection (force exit if shutdown hangs)
117
+ * - Double-shutdown prevention
118
+ *
119
+ * @example
120
+ * installProcessHandlers({
121
+ * onUncaughtException: (error) => sendToMonitoring(error),
122
+ * onUnhandledRejection: (reason) => sendToMonitoring(reason),
123
+ * onShutdown: async () => {
124
+ * await database.close();
125
+ * await server.close();
126
+ * },
127
+ * shutdownTimeout: 15000,
128
+ * });
129
+ */
130
+ function installProcessHandlers(options) {
131
+ // Error handlers
132
+ process.on('uncaughtException', (error) => {
133
+ options?.onUncaughtException?.(error);
134
+ console.error('CRITICAL - Uncaught Exception:', error);
135
+ });
136
+ process.on('unhandledRejection', (reason) => {
137
+ options?.onUnhandledRejection?.(reason);
138
+ console.error('CRITICAL - Unhandled Rejection:', reason);
139
+ });
140
+ // Graceful shutdown
141
+ const onShutdown = options?.onShutdown;
142
+ if (onShutdown) {
143
+ let isShuttingDown = false;
144
+ const signals = options?.shutdownSignals ?? ['SIGTERM', 'SIGINT'];
145
+ const timeout = options?.shutdownTimeout ?? 10000;
146
+ const shutdown = async (signal) => {
147
+ if (isShuttingDown)
148
+ return;
149
+ isShuttingDown = true;
150
+ console.log(`${signal} received, shutting down gracefully...`);
151
+ const timer = setTimeout(() => {
152
+ console.error('Shutdown timeout, forcing exit');
153
+ process.exit(1);
154
+ }, timeout);
155
+ try {
156
+ await onShutdown();
157
+ clearTimeout(timer);
158
+ process.exit(0);
159
+ }
160
+ catch (err) {
161
+ console.error('Shutdown error:', err);
162
+ clearTimeout(timer);
163
+ process.exit(1);
164
+ }
165
+ };
166
+ for (const signal of signals) {
167
+ process.on(signal, () => shutdown(signal));
168
+ }
169
+ }
170
+ }
71
171
  //# sourceMappingURL=error-handling.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-handling.js","sourceRoot":"","sources":["../../src/error-handling.ts"],"names":[],"mappings":";;AAoCA,4CAeC;AAMD,gDAkBC;AA3ED,mDAAyD;AAEzD,2CAAqD;AACrD,2DAAmF;AACnF,8EAA6E;AAC7E,+DAA6D;AAG7D,SAAS,gBAAgB,CAAC,KAAc;IACtC,MAAM,GAAG,GAAG,IAAA,6CAAsB,GAAE,CAAC;IACrC,MAAM,SAAS,GAAG,GAAG,EAAE,SAAS,CAAC;IACjC,IAAI,QAA0C,CAAC;IAE/C,IAAI,KAAK,YAAY,qBAAS,EAAE,CAAC;QAC/B,QAAQ,GAAG;YACT,GAAG,IAAA,oCAAiB,EAAC,SAAS,CAAC;YAC/B,KAAK,EAAE,KAAK,CAAC,OAAO;YACpB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,MAAM,YAAY,GAAG,IAAA,gCAAmB,EAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACpD,QAAQ,GAAG;YACT,GAAG,IAAA,oCAAiB,EAAC,SAAS,CAAC;YAC/B,KAAK,EAAE,YAAY,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,gBAAgB;YAChF,MAAM,EAAE,8CAA0B;SACnC,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACd,QAAQ,CAAC,SAAS,GAAG,SAAS,CAAC;IACjC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,8EAA8E;AAC9E,SAAgB,gBAAgB,CAAC,EAAmB;IAClD,OAAO,KAAK,EAAE,GAAmB,EAAE,GAAoB,EAAE,IAAkB,EAAiB,EAAE;QAC5F,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,WAAW,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;YAC5C,IAAI,WAAW,CAAC,MAAM,IAAI,8CAA0B,EAAE,CAAC;gBACrD,OAAO,CAAC,KAAK,CAAC,qBAAqB,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;YACxD,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,qBAAqB,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;YACtD,CAAC;YACD,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAC/B,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,kBAAkB,CACtC,KAAc,EACd,CAAiB,EACjB,GAAoB,EACpB,IAAkB;IAElB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,EAAE,CAAC;QACP,OAAO;IACT,CAAC;IACD,8DAA8D;IAC9D,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,IAAA,gCAAmB,EAAC,KAAK,CAAC,CAAC,CAAC;IACjE,MAAM,WAAW,GACf,KAAK,YAAY,WAAW,CAAC,2BAA2B;QACtD,CAAC,CAAC,gBAAgB,CAAC,GAAG,oCAAgB,8BAA8B,KAAK,CAAC,OAAO,EAAE,CAAC;QACpF,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAC9B,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AACxB,CAAC"}
1
+ {"version":3,"file":"error-handling.js","sourceRoot":"","sources":["../../src/error-handling.ts"],"names":[],"mappings":"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"}
@@ -13,3 +13,4 @@ export * from './router';
13
13
  export * from './thread-local/thread-local-storage';
14
14
  export * from './thread-local/thread-local-storage-middleware';
15
15
  export * from './utils/express.utils';
16
+ export * from './utils/type-validators';
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
@@ -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,13 +8,20 @@ import { ExpressRouter } from './utils/express.utils';
7
8
  export declare class RouteTable {
8
9
  private readonly app;
9
10
  constructor(app: ExpressRouter);
10
- get<T>(path: string, endpoint: GetEndpoint<T> | GetEndpoint<T[]>): this;
11
- get<T>(path: string, run: (ctx: RequestContext) => Promise<ResponseOrValue<T>>): this;
12
- post<Body, Result>(path: string, endpoint: PostEndpoint<Body, Result>): this;
13
- patch<Body, Result>(path: string, endpoint: PatchEndpoint<Body, Result>): this;
14
- put<Body, Result>(path: string, endpoint: PutEndpoint<Body, Result>): this;
15
- delete(path: string, endpoint: DeleteEndpoint): this;
16
- delete(path: string, run: (ctx: RequestContext) => Promise<void>): this;
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. */
24
+ delete(path: string, run: (ctx: RequestContext) => void | Promise<void>): this;
17
25
  }
18
26
  /**
19
27
  * Factory function to create a new route table.
@@ -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":";;;AA+DA,4CAEC;AAjED,qCAakB;AAGlB;;;GAGG;AACH,MAAa,UAAU;IACrB,YAA6B,GAAkB;QAAlB,QAAG,GAAH,GAAG,CAAe;IAAG,CAAC;IAInD,GAAG,CACD,IAAY,EACZ,aAAyG;QAEzG,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,QAA0B,CAAC,CAAC;QACrD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAe,IAAY,EAAE,QAAoC;QACnE,IAAA,kBAAS,EAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAe,IAAY,EAAE,QAAqC;QACrE,IAAA,mBAAU,EAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,CAAe,IAAY,EAAE,QAAmC;QACjE,IAAA,iBAAQ,EAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAID,MAAM,CAAC,IAAY,EAAE,aAAwE;QAC3F,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,QAAQ,CAAC,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AApCD,gCAoCC;AAED;;;;GAIG;AACH,SAAgB,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":";;;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"}