@fishka/express 0.9.5 → 0.9.7
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/dist/cjs/api.types.d.ts +83 -0
- package/dist/cjs/api.types.js +38 -2
- package/dist/cjs/auth/auth-strategy.d.ts +42 -0
- package/dist/cjs/auth/auth-strategy.js +2 -2
- package/dist/cjs/auth/auth.types.d.ts +42 -0
- package/dist/cjs/auth/auth.utils.d.ts +31 -0
- package/dist/cjs/auth/auth.utils.js +3 -3
- package/dist/cjs/auth/bearer-auth-strategy.d.ts +38 -0
- package/dist/cjs/auth/bearer-auth-strategy.js +2 -2
- package/dist/cjs/config.d.ts +23 -0
- package/dist/cjs/error-handling.d.ts +9 -0
- package/dist/cjs/error-handling.js +4 -4
- package/dist/cjs/http-status-codes.d.ts +179 -0
- package/dist/cjs/http-status-codes.js +228 -0
- package/dist/cjs/index.d.ts +15 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/rate-limit/in-memory-rate-limiter.d.ts +35 -0
- package/dist/cjs/rate-limit/rate-limit.d.ts +19 -0
- package/dist/cjs/rate-limit/rate-limit.types.d.ts +23 -0
- package/dist/cjs/route-table.d.ts +23 -0
- package/dist/cjs/router.d.ts +110 -0
- package/dist/cjs/router.js +11 -11
- package/dist/cjs/thread-local/thread-local-storage-middleware.d.ts +9 -0
- package/dist/cjs/thread-local/thread-local-storage.d.ts +25 -0
- package/dist/cjs/utils/conversion.utils.d.ts +14 -0
- package/dist/cjs/utils/express.utils.d.ts +7 -0
- package/dist/esm/api.types.d.ts +83 -0
- package/dist/esm/api.types.js +37 -2
- package/dist/esm/auth/auth-strategy.d.ts +42 -0
- package/dist/esm/auth/auth-strategy.js +2 -2
- package/dist/esm/auth/auth.types.d.ts +42 -0
- package/dist/esm/auth/auth.utils.d.ts +31 -0
- package/dist/esm/auth/auth.utils.js +3 -3
- package/dist/esm/auth/bearer-auth-strategy.d.ts +38 -0
- package/dist/esm/auth/bearer-auth-strategy.js +2 -2
- package/dist/esm/config.d.ts +23 -0
- package/dist/esm/error-handling.d.ts +9 -0
- package/dist/esm/error-handling.js +4 -4
- package/dist/esm/http-status-codes.d.ts +179 -0
- package/dist/esm/http-status-codes.js +224 -0
- package/dist/esm/index.d.ts +15 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/rate-limit/in-memory-rate-limiter.d.ts +35 -0
- package/dist/esm/rate-limit/rate-limit.d.ts +19 -0
- package/dist/esm/rate-limit/rate-limit.types.d.ts +23 -0
- package/dist/esm/route-table.d.ts +23 -0
- package/dist/esm/router.d.ts +110 -0
- package/dist/esm/router.js +12 -12
- package/dist/esm/thread-local/thread-local-storage-middleware.d.ts +9 -0
- package/dist/esm/thread-local/thread-local-storage.d.ts +25 -0
- package/dist/esm/utils/conversion.utils.d.ts +14 -0
- package/dist/esm/utils/express.utils.d.ts +7 -0
- package/package.json +1 -1
- package/dist/cjs/http.types.js +0 -38
- package/dist/esm/http.types.js +0 -35
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getHttpStatusCodeReasonPhrase = exports.HttpStatusCode = exports.HTTP_GATEWAY_TIMEOUT = exports.HTTP_SERVICE_UNAVAILABLE = exports.HTTP_BAD_GATEWAY = exports.HTTP_NOT_IMPLEMENTED = exports.HTTP_INTERNAL_SERVER_ERROR = exports.HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = exports.HTTP_CONNECTION_CLOSED_WITHOUT_RESPONSE = exports.HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = exports.HTTP_TOO_MANY_REQUESTS = exports.HTTP_PRECONDITION_REQUIRED = exports.HTTP_UPGRADE_REQUIRED = exports.HTTP_TOO_EARLY = exports.HTTP_FAILED_DEPENDENCY = exports.HTTP_LOCKED = exports.HTTP_UNPROCESSABLE_ENTITY = exports.HTTP_IM_A_TEAPOT = exports.HTTP_EXPECTATION_FAILED = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_UNSUPPORTED_MEDIA_TYPE = exports.HTTP_URI_TOO_LONG = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_PRECONDITION_FAILED = exports.HTTP_GONE = exports.HTTP_CONFLICT = exports.HTTP_REQUEST_TIMEOUT = exports.HTTP_PROXY_AUTHENTICATION_REQUIRED = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_FOUND = exports.HTTP_MOVED_PERMANENTLY = exports.HTTP_IM_USED = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_RESET_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_ACCEPTED = exports.HTTP_CREATED = exports.HTTP_OK = void 0;
|
|
4
|
+
/** The request has succeeded (200) */
|
|
5
|
+
exports.HTTP_OK = 200;
|
|
6
|
+
/** The request has been fulfilled, and a new resource is created (201) */
|
|
7
|
+
exports.HTTP_CREATED = 201;
|
|
8
|
+
/** The request has been accepted for processing, but the processing is not yet complete (202) */
|
|
9
|
+
exports.HTTP_ACCEPTED = 202;
|
|
10
|
+
/** The server successfully processed the request, and is not returning any content (204) */
|
|
11
|
+
exports.HTTP_NO_CONTENT = 204;
|
|
12
|
+
/** The request has been successfully processed, but is returning no content (205) */
|
|
13
|
+
exports.HTTP_RESET_CONTENT = 205;
|
|
14
|
+
/** The server is delivering only part of the resource due to a range header sent by the client (206) */
|
|
15
|
+
exports.HTTP_PARTIAL_CONTENT = 206;
|
|
16
|
+
/** The IM used the server processed a request successfully, but the response is transformed (226) */
|
|
17
|
+
exports.HTTP_IM_USED = 226;
|
|
18
|
+
/** The resource has been moved permanently to a new URL (301) */
|
|
19
|
+
exports.HTTP_MOVED_PERMANENTLY = 301;
|
|
20
|
+
/** The resource is available at a different URL (302) */
|
|
21
|
+
exports.HTTP_FOUND = 302;
|
|
22
|
+
/** The resource has not been modified since the last request (304) */
|
|
23
|
+
exports.HTTP_NOT_MODIFIED = 304;
|
|
24
|
+
/** The server could not understand the request due to invalid syntax (400) */
|
|
25
|
+
exports.HTTP_BAD_REQUEST = 400;
|
|
26
|
+
/** The client must authenticate itself to get the requested response (401) */
|
|
27
|
+
exports.HTTP_UNAUTHORIZED = 401;
|
|
28
|
+
/** The client does not have access rights to the content (403) */
|
|
29
|
+
exports.HTTP_FORBIDDEN = 403;
|
|
30
|
+
/** The server cannot find the requested resource (404) */
|
|
31
|
+
exports.HTTP_NOT_FOUND = 404;
|
|
32
|
+
/** The request method is known by the server but is not supported by the target resource (405) */
|
|
33
|
+
exports.HTTP_METHOD_NOT_ALLOWED = 405;
|
|
34
|
+
/** The client must authenticate using a proxy (407) */
|
|
35
|
+
exports.HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
|
|
36
|
+
/** The server timed out waiting for the request (408) */
|
|
37
|
+
exports.HTTP_REQUEST_TIMEOUT = 408;
|
|
38
|
+
/** The request conflicts with the current state of the server (409) */
|
|
39
|
+
exports.HTTP_CONFLICT = 409;
|
|
40
|
+
/** The resource requested is no longer available and will not be available again (410) */
|
|
41
|
+
exports.HTTP_GONE = 410;
|
|
42
|
+
/** The request does not meet the preconditions that the server requires (412) */
|
|
43
|
+
exports.HTTP_PRECONDITION_FAILED = 412;
|
|
44
|
+
/** The client sent a request that is too large for the server to process (413) */
|
|
45
|
+
exports.HTTP_PAYLOAD_TOO_LARGE = 413;
|
|
46
|
+
/** The URI requested by the client is too long for the server to process (414) */
|
|
47
|
+
exports.HTTP_URI_TOO_LONG = 414;
|
|
48
|
+
/** The media format of the requested data is not supported by the server (415) */
|
|
49
|
+
exports.HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
|
|
50
|
+
/** The range specified by the client cannot be fulfilled (416) */
|
|
51
|
+
exports.HTTP_RANGE_NOT_SATISFIABLE = 416;
|
|
52
|
+
/** The expectation given in the request header could not be met by the server (417) */
|
|
53
|
+
exports.HTTP_EXPECTATION_FAILED = 417;
|
|
54
|
+
/** The server refuses to brew coffee because it is a teapot (418) - an Easter egg from RFC 2324 */
|
|
55
|
+
exports.HTTP_IM_A_TEAPOT = 418;
|
|
56
|
+
/** The request was well-formed but was unable to be followed due to semantic errors (422) */
|
|
57
|
+
exports.HTTP_UNPROCESSABLE_ENTITY = 422;
|
|
58
|
+
/** The resource that is being accessed is locked (423) */
|
|
59
|
+
exports.HTTP_LOCKED = 423;
|
|
60
|
+
/** The resource requested is dependent on another resource that has failed (424) */
|
|
61
|
+
exports.HTTP_FAILED_DEPENDENCY = 424;
|
|
62
|
+
/** The server is unwilling to risk processing a request that might be replayed (425) */
|
|
63
|
+
exports.HTTP_TOO_EARLY = 425;
|
|
64
|
+
/** The client needs to upgrade its protocol (426) */
|
|
65
|
+
exports.HTTP_UPGRADE_REQUIRED = 426;
|
|
66
|
+
/** The server requires the request to be conditional (428) */
|
|
67
|
+
exports.HTTP_PRECONDITION_REQUIRED = 428;
|
|
68
|
+
/** The client has sent too many requests in a given amount of time (429) */
|
|
69
|
+
exports.HTTP_TOO_MANY_REQUESTS = 429;
|
|
70
|
+
/** The request header fields are too large (431) */
|
|
71
|
+
exports.HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
|
|
72
|
+
/** The client closed the connection with the server before the request was completed (444) */
|
|
73
|
+
exports.HTTP_CONNECTION_CLOSED_WITHOUT_RESPONSE = 444;
|
|
74
|
+
/** The client requested an unavailable legal action (451) */
|
|
75
|
+
exports.HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
|
|
76
|
+
/** The server encountered an internal error and was unable to complete the request (500) */
|
|
77
|
+
exports.HTTP_INTERNAL_SERVER_ERROR = 500;
|
|
78
|
+
/** The request method is not supported by the server and cannot be handled (501) */
|
|
79
|
+
exports.HTTP_NOT_IMPLEMENTED = 501;
|
|
80
|
+
/** The server, while acting as a gateway or proxy, received an invalid response from the upstream server (502) */
|
|
81
|
+
exports.HTTP_BAD_GATEWAY = 502;
|
|
82
|
+
/** The server is not ready to handle the request, typically due to temporary overload or maintenance (503) */
|
|
83
|
+
exports.HTTP_SERVICE_UNAVAILABLE = 503;
|
|
84
|
+
/** The server, while acting as a gateway or proxy, did not get a response in time from the upstream server (504) */
|
|
85
|
+
exports.HTTP_GATEWAY_TIMEOUT = 504;
|
|
86
|
+
/**
|
|
87
|
+
* An object containing common HTTP status codes.
|
|
88
|
+
* These constants can be used to improve code readability and maintainability.
|
|
89
|
+
* @deprecated Use individual HTTP_* constants for better tree-shaking
|
|
90
|
+
*/
|
|
91
|
+
exports.HttpStatusCode = {
|
|
92
|
+
/** The request has succeeded (200) */
|
|
93
|
+
OK: exports.HTTP_OK,
|
|
94
|
+
/** The request has been fulfilled, and a new resource is created (201) */
|
|
95
|
+
CREATED: exports.HTTP_CREATED,
|
|
96
|
+
/** The request has been accepted for processing, but the processing is not yet complete (202) */
|
|
97
|
+
ACCEPTED: exports.HTTP_ACCEPTED,
|
|
98
|
+
/** The server successfully processed the request, and is not returning any content (204) */
|
|
99
|
+
NO_CONTENT: exports.HTTP_NO_CONTENT,
|
|
100
|
+
/** The request has been successfully processed, but is returning no content (205) */
|
|
101
|
+
RESET_CONTENT: exports.HTTP_RESET_CONTENT,
|
|
102
|
+
/** The server is delivering only part of the resource due to a range header sent by the client (206) */
|
|
103
|
+
PARTIAL_CONTENT: exports.HTTP_PARTIAL_CONTENT,
|
|
104
|
+
/** The IM used the server processed a request successfully, but the response is transformed (226) */
|
|
105
|
+
IM_USED: exports.HTTP_IM_USED,
|
|
106
|
+
/** The resource has been moved permanently to a new URL (301) */
|
|
107
|
+
MOVED_PERMANENTLY: exports.HTTP_MOVED_PERMANENTLY,
|
|
108
|
+
/** The resource is available at a different URL (302) */
|
|
109
|
+
FOUND: exports.HTTP_FOUND,
|
|
110
|
+
/** The resource has not been modified since the last request (304) */
|
|
111
|
+
NOT_MODIFIED: exports.HTTP_NOT_MODIFIED,
|
|
112
|
+
/** The server could not understand the request due to invalid syntax (400) */
|
|
113
|
+
BAD_REQUEST: exports.HTTP_BAD_REQUEST,
|
|
114
|
+
/** The client must authenticate itself to get the requested response (401) */
|
|
115
|
+
UNAUTHORIZED: exports.HTTP_UNAUTHORIZED,
|
|
116
|
+
/** The client does not have access rights to the content (403) */
|
|
117
|
+
FORBIDDEN: exports.HTTP_FORBIDDEN,
|
|
118
|
+
/** The server cannot find the requested resource (404) */
|
|
119
|
+
NOT_FOUND: exports.HTTP_NOT_FOUND,
|
|
120
|
+
/** The request method is known by the server but is not supported by the target resource (405) */
|
|
121
|
+
METHOD_NOT_ALLOWED: exports.HTTP_METHOD_NOT_ALLOWED,
|
|
122
|
+
/** The client must authenticate using a proxy (407) */
|
|
123
|
+
PROXY_AUTHENTICATION_REQUIRED: exports.HTTP_PROXY_AUTHENTICATION_REQUIRED,
|
|
124
|
+
/** The server timed out waiting for the request (408) */
|
|
125
|
+
REQUEST_TIMEOUT: exports.HTTP_REQUEST_TIMEOUT,
|
|
126
|
+
/** The request conflicts with the current state of the server (409) */
|
|
127
|
+
CONFLICT: exports.HTTP_CONFLICT,
|
|
128
|
+
/** The resource requested is no longer available and will not be available again (410) */
|
|
129
|
+
GONE: exports.HTTP_GONE,
|
|
130
|
+
/** The request does not meet the preconditions that the server requires (412) */
|
|
131
|
+
PRECONDITION_FAILED: exports.HTTP_PRECONDITION_FAILED,
|
|
132
|
+
/** The client sent a request that is too large for the server to process (413) */
|
|
133
|
+
PAYLOAD_TOO_LARGE: exports.HTTP_PAYLOAD_TOO_LARGE,
|
|
134
|
+
/** The URI requested by the client is too long for the server to process (414) */
|
|
135
|
+
URI_TOO_LONG: exports.HTTP_URI_TOO_LONG,
|
|
136
|
+
/** The media format of the requested data is not supported by the server (415) */
|
|
137
|
+
UNSUPPORTED_MEDIA_TYPE: exports.HTTP_UNSUPPORTED_MEDIA_TYPE,
|
|
138
|
+
/** The range specified by the client cannot be fulfilled (416) */
|
|
139
|
+
RANGE_NOT_SATISFIABLE: exports.HTTP_RANGE_NOT_SATISFIABLE,
|
|
140
|
+
/** The expectation given in the request header could not be met by the server (417) */
|
|
141
|
+
EXPECTATION_FAILED: exports.HTTP_EXPECTATION_FAILED,
|
|
142
|
+
/** The server refuses to brew coffee because it is a teapot (418) - an Easter egg from RFC 2324 */
|
|
143
|
+
IM_A_TEAPOT: exports.HTTP_IM_A_TEAPOT,
|
|
144
|
+
/** The request was well-formed but was unable to be followed due to semantic errors (422) */
|
|
145
|
+
UNPROCESSABLE_ENTITY: exports.HTTP_UNPROCESSABLE_ENTITY,
|
|
146
|
+
/** The resource that is being accessed is locked (423) */
|
|
147
|
+
LOCKED: exports.HTTP_LOCKED,
|
|
148
|
+
/** The resource requested is dependent on another resource that has failed (424) */
|
|
149
|
+
FAILED_DEPENDENCY: exports.HTTP_FAILED_DEPENDENCY,
|
|
150
|
+
/** The server is unwilling to risk processing a request that might be replayed (425) */
|
|
151
|
+
TOO_EARLY: exports.HTTP_TOO_EARLY,
|
|
152
|
+
/** The client needs to upgrade its protocol (426) */
|
|
153
|
+
UPGRADE_REQUIRED: exports.HTTP_UPGRADE_REQUIRED,
|
|
154
|
+
/** The server requires the request to be conditional (428) */
|
|
155
|
+
PRECONDITION_REQUIRED: exports.HTTP_PRECONDITION_REQUIRED,
|
|
156
|
+
/** The client has sent too many requests in a given amount of time (429) */
|
|
157
|
+
TOO_MANY_REQUESTS: exports.HTTP_TOO_MANY_REQUESTS,
|
|
158
|
+
/** The request header fields are too large (431) */
|
|
159
|
+
REQUEST_HEADER_FIELDS_TOO_LARGE: exports.HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
|
|
160
|
+
/** The client closed the connection with the server before the request was completed (444) */
|
|
161
|
+
CONNECTION_CLOSED_WITHOUT_RESPONSE: exports.HTTP_CONNECTION_CLOSED_WITHOUT_RESPONSE,
|
|
162
|
+
/** The client requested an unavailable legal action (451) */
|
|
163
|
+
UNAVAILABLE_FOR_LEGAL_REASONS: exports.HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
|
|
164
|
+
/** The server encountered an internal error and was unable to complete the request (500) */
|
|
165
|
+
INTERNAL_SERVER_ERROR: exports.HTTP_INTERNAL_SERVER_ERROR,
|
|
166
|
+
/** The request method is not supported by the server and cannot be handled (501) */
|
|
167
|
+
NOT_IMPLEMENTED: exports.HTTP_NOT_IMPLEMENTED,
|
|
168
|
+
/** The server, while acting as a gateway or proxy, received an invalid response from the upstream server (502) */
|
|
169
|
+
BAD_GATEWAY: exports.HTTP_BAD_GATEWAY,
|
|
170
|
+
/** The server is not ready to handle the request, typically due to temporary overload or maintenance (503) */
|
|
171
|
+
SERVICE_UNAVAILABLE: exports.HTTP_SERVICE_UNAVAILABLE,
|
|
172
|
+
/** The server, while acting as a gateway or proxy, did not get a response in time from the upstream server (504) */
|
|
173
|
+
GATEWAY_TIMEOUT: exports.HTTP_GATEWAY_TIMEOUT,
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Returns the reason phrase corresponding to the given HTTP status code.
|
|
177
|
+
* This is useful for converting status codes into human-readable messages.
|
|
178
|
+
*
|
|
179
|
+
* @param statusCode - The HTTP status code for which to get the reason phrase.
|
|
180
|
+
* @returns The reason phrase associated with the given status code, or "Unknown Status Code" if the status code is not recognized.
|
|
181
|
+
*/
|
|
182
|
+
const getHttpStatusCodeReasonPhrase = (statusCode) => {
|
|
183
|
+
const phrases = {
|
|
184
|
+
200: 'OK',
|
|
185
|
+
201: 'Created',
|
|
186
|
+
202: 'Accepted',
|
|
187
|
+
204: 'No Content',
|
|
188
|
+
205: 'Reset Content',
|
|
189
|
+
206: 'Partial Content',
|
|
190
|
+
226: 'IM Used',
|
|
191
|
+
301: 'Moved Permanently',
|
|
192
|
+
302: 'Found',
|
|
193
|
+
304: 'Not Modified',
|
|
194
|
+
400: 'Bad Request',
|
|
195
|
+
401: 'Unauthorized',
|
|
196
|
+
403: 'Forbidden',
|
|
197
|
+
404: 'Not Found',
|
|
198
|
+
405: 'Method Not Allowed',
|
|
199
|
+
407: 'Proxy Authentication Required',
|
|
200
|
+
408: 'Request Timeout',
|
|
201
|
+
409: 'Conflict',
|
|
202
|
+
410: 'Gone',
|
|
203
|
+
412: 'Precondition Failed',
|
|
204
|
+
413: 'Payload Too Large',
|
|
205
|
+
414: 'URI Too Long',
|
|
206
|
+
415: 'Unsupported Media Type',
|
|
207
|
+
416: 'Range Not Satisfiable',
|
|
208
|
+
417: 'Expectation Failed',
|
|
209
|
+
418: "I'm a teapot",
|
|
210
|
+
422: 'Unprocessable Entity',
|
|
211
|
+
423: 'Locked',
|
|
212
|
+
424: 'Failed Dependency',
|
|
213
|
+
425: 'Too Early',
|
|
214
|
+
426: 'Upgrade Required',
|
|
215
|
+
428: 'Precondition Required',
|
|
216
|
+
429: 'Too Many Requests',
|
|
217
|
+
431: 'Request Header Fields Too Large',
|
|
218
|
+
444: 'Connection Closed Without Response',
|
|
219
|
+
451: 'Unavailable For Legal Reasons',
|
|
220
|
+
500: 'Internal Server Error',
|
|
221
|
+
501: 'Not Implemented',
|
|
222
|
+
502: 'Bad Gateway',
|
|
223
|
+
503: 'Service Unavailable',
|
|
224
|
+
504: 'Gateway Timeout',
|
|
225
|
+
};
|
|
226
|
+
return phrases[statusCode] || 'Unknown Status Code';
|
|
227
|
+
};
|
|
228
|
+
exports.getHttpStatusCodeReasonPhrase = getHttpStatusCodeReasonPhrase;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from './api.types';
|
|
2
|
+
export * from './auth/auth-strategy';
|
|
3
|
+
export * from './auth/auth.types';
|
|
4
|
+
export * from './auth/auth.utils';
|
|
5
|
+
export * from './auth/bearer-auth-strategy';
|
|
6
|
+
export * from './config';
|
|
7
|
+
export * from './error-handling';
|
|
8
|
+
export * from './http-status-codes';
|
|
9
|
+
export * from './rate-limit/in-memory-rate-limiter';
|
|
10
|
+
export * from './rate-limit/rate-limit.types';
|
|
11
|
+
export * from './route-table';
|
|
12
|
+
export * from './router';
|
|
13
|
+
export * from './thread-local/thread-local-storage';
|
|
14
|
+
export * from './thread-local/thread-local-storage-middleware';
|
|
15
|
+
export * from './utils/express.utils';
|
package/dist/cjs/index.js
CHANGED
|
@@ -21,7 +21,7 @@ __exportStar(require("./auth/auth.utils"), exports);
|
|
|
21
21
|
__exportStar(require("./auth/bearer-auth-strategy"), exports);
|
|
22
22
|
__exportStar(require("./config"), exports);
|
|
23
23
|
__exportStar(require("./error-handling"), exports);
|
|
24
|
-
__exportStar(require("./http
|
|
24
|
+
__exportStar(require("./http-status-codes"), exports);
|
|
25
25
|
__exportStar(require("./rate-limit/in-memory-rate-limiter"), exports);
|
|
26
26
|
__exportStar(require("./rate-limit/rate-limit.types"), exports);
|
|
27
27
|
__exportStar(require("./route-table"), exports);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ExpressFunction } from '../utils/express.utils';
|
|
2
|
+
import { RateLimitConfig, RateLimitResult } from './rate-limit.types';
|
|
3
|
+
/**
|
|
4
|
+
* In-memory rate limiter using sliding window counter.
|
|
5
|
+
* Tracks request counts per key with time-based window expiration.
|
|
6
|
+
*/
|
|
7
|
+
declare class InMemoryRateLimiter {
|
|
8
|
+
private readonly limits;
|
|
9
|
+
private readonly points;
|
|
10
|
+
private readonly durationMs;
|
|
11
|
+
constructor(points: number, durationSeconds: number);
|
|
12
|
+
/**
|
|
13
|
+
* Try to consume points from the rate limit.
|
|
14
|
+
* Returns result if successful, throws if limit exceeded.
|
|
15
|
+
*
|
|
16
|
+
* @param key - Unique identifier for the client (e.g., IP address)
|
|
17
|
+
* @returns Rate limit result with remaining points and ms until reset
|
|
18
|
+
* @throws RateLimitResult if limit exceeded
|
|
19
|
+
*/
|
|
20
|
+
consume(key: string): RateLimitResult;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Creates a rate limiter middleware using in-memory implementation.
|
|
24
|
+
*
|
|
25
|
+
* Separate limiters are used for read (GET) and write (POST/PATCH/PUT/DELETE) requests.
|
|
26
|
+
*
|
|
27
|
+
* @param config - Rate limit configuration
|
|
28
|
+
* @returns Express middleware function
|
|
29
|
+
*/
|
|
30
|
+
export declare function createRateLimiterMiddleware(config: RateLimitConfig): Promise<ExpressFunction>;
|
|
31
|
+
/**
|
|
32
|
+
* Export the in-memory limiter class for advanced use cases where
|
|
33
|
+
* you might want direct control over rate limit management.
|
|
34
|
+
*/
|
|
35
|
+
export { InMemoryRateLimiter };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ExpressResponse } from '../utils/express.utils';
|
|
2
|
+
import { RateLimitResult } from './rate-limit.types';
|
|
3
|
+
/**
|
|
4
|
+
* Adds rate limit state headers to the response.
|
|
5
|
+
* See https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/
|
|
6
|
+
*
|
|
7
|
+
* Headers added:
|
|
8
|
+
* - X-RateLimit-Limit: Server's quota for requests in the time window
|
|
9
|
+
* - X-RateLimit-Remaining: Remaining quota in the current window
|
|
10
|
+
* - X-RateLimit-Reset: Time remaining in the current window (in seconds)
|
|
11
|
+
* - X-RateLimit-Policy: Quota policies associated with the client
|
|
12
|
+
* @Internal
|
|
13
|
+
*/
|
|
14
|
+
export declare function addRateLimitHeaders(res: ExpressResponse, result: RateLimitResult, limitPoints: number, duration: number): ExpressResponse;
|
|
15
|
+
/**
|
|
16
|
+
* Converts milliseconds to seconds, rounding up.
|
|
17
|
+
* @Internal
|
|
18
|
+
*/
|
|
19
|
+
export declare function msToSeconds(ms: number): number;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for rate limiting.
|
|
3
|
+
*/
|
|
4
|
+
export interface RateLimitConfig {
|
|
5
|
+
/** Rate limit points per time window */
|
|
6
|
+
points: {
|
|
7
|
+
read: number;
|
|
8
|
+
write: number;
|
|
9
|
+
};
|
|
10
|
+
/** Duration of the rate limit window in seconds */
|
|
11
|
+
duration: number;
|
|
12
|
+
/** Key prefix for rate limit data (optional, defaults to 'rate_limit') */
|
|
13
|
+
keyPrefix?: string;
|
|
14
|
+
/** Paths that should bypass rate limiting (default: ['/v1', '/health']) */
|
|
15
|
+
rateLimitWhitelist?: string[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Simple in-memory rate limiter result.
|
|
19
|
+
*/
|
|
20
|
+
export interface RateLimitResult {
|
|
21
|
+
remainingPoints: number;
|
|
22
|
+
msBeforeNext: number;
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ExpressApplication } from './utils/express.utils';
|
|
2
|
+
import { DeleteEndpoint, GetEndpoint, PatchEndpoint, PostEndpoint, PutEndpoint, RequestContext, ResponseOrValue } from './router';
|
|
3
|
+
/**
|
|
4
|
+
* Helper utility for organizing and mounting routes.
|
|
5
|
+
* Provides a fluent interface for registering multiple handlers.
|
|
6
|
+
*/
|
|
7
|
+
export declare class RouteTable {
|
|
8
|
+
private readonly app;
|
|
9
|
+
constructor(app: ExpressApplication);
|
|
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;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Factory function to create a new route table.
|
|
20
|
+
* @param app Express application instance
|
|
21
|
+
* @returns RouteTable instance with fluent API
|
|
22
|
+
*/
|
|
23
|
+
export declare function createRouteTable(app: ExpressApplication): RouteTable;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Assertion, ObjectAssertion } from '@fishka/assertions';
|
|
2
|
+
import { ApiResponse, UrlTokensValidator } from './api.types';
|
|
3
|
+
import { AuthUser } from './auth/auth.types';
|
|
4
|
+
import { ExpressApplication, ExpressRequest, ExpressResponse } from './utils/express.utils';
|
|
5
|
+
/** Express API allows handlers to return response in the raw form. */
|
|
6
|
+
export type ResponseOrValue<ResponseEntity> = ApiResponse<ResponseEntity> | ResponseEntity;
|
|
7
|
+
/**
|
|
8
|
+
* Generic middleware hook for endpoint execution.
|
|
9
|
+
* Allows custom logic like transaction management, authorization checks, etc.
|
|
10
|
+
*/
|
|
11
|
+
export type EndpointMiddleware<Context = RequestContext> = (run: () => Promise<unknown>, context: Context) => Promise<unknown>;
|
|
12
|
+
/** Generic request context passed to all handlers. Database-agnostic and extensible. */
|
|
13
|
+
export interface RequestContext<Body = void> {
|
|
14
|
+
/** Parsed and validated request body (for POST/PATCH/PUT handlers). */
|
|
15
|
+
body: Body;
|
|
16
|
+
/** Express Request object. */
|
|
17
|
+
req: ExpressRequest;
|
|
18
|
+
/** Express Response object. */
|
|
19
|
+
res: ExpressResponse;
|
|
20
|
+
/** Authenticated user (if any). Populated by auth middleware. */
|
|
21
|
+
authUser?: AuthUser;
|
|
22
|
+
/**
|
|
23
|
+
* Generic parameter access with lazy validation.
|
|
24
|
+
* Provides type-safe access to URL path and query parameters.
|
|
25
|
+
*/
|
|
26
|
+
params: {
|
|
27
|
+
get(key: string): string;
|
|
28
|
+
tryGet(key: string): string | undefined;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Query parameter access.
|
|
32
|
+
*/
|
|
33
|
+
query: {
|
|
34
|
+
get(key: string): string | undefined;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Generic state storage for middleware to attach data.
|
|
38
|
+
* Allows middleware to pass information to handlers and other middleware.
|
|
39
|
+
*/
|
|
40
|
+
state: Map<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
/** Base interface with common endpoint properties. */
|
|
43
|
+
export interface EndpointBase<Context = RequestContext, Result = unknown> {
|
|
44
|
+
/** Path parameter validator. */
|
|
45
|
+
$path?: UrlTokensValidator;
|
|
46
|
+
/** Query parameter validator. */
|
|
47
|
+
$query?: UrlTokensValidator;
|
|
48
|
+
/** Optional middleware to execute before the handler. */
|
|
49
|
+
middlewares?: Array<EndpointMiddleware>;
|
|
50
|
+
/** Handler function. */
|
|
51
|
+
run: (ctx: Context) => Promise<ResponseOrValue<Result>>;
|
|
52
|
+
}
|
|
53
|
+
/** Descriptor for GET list routes. */
|
|
54
|
+
export type GetListEndpoint<ResultElementType = unknown> = EndpointBase<RequestContext, Array<ResultElementType>>;
|
|
55
|
+
/** Descriptor for GET routes. */
|
|
56
|
+
export type GetEndpoint<Result = unknown> = EndpointBase<RequestContext, Result>;
|
|
57
|
+
/** Descriptor for POST routes. */
|
|
58
|
+
export interface PostEndpoint<Body = unknown, Result = unknown> extends EndpointBase<RequestContext<Body>, Result> {
|
|
59
|
+
/** Request body validator. */
|
|
60
|
+
$body: Body extends object ? ObjectAssertion<Body> : Assertion<Body>;
|
|
61
|
+
}
|
|
62
|
+
/** Same as POST. Used for full object updates. */
|
|
63
|
+
export type PutEndpoint<Body = unknown, Result = unknown> = PostEndpoint<Body, Result>;
|
|
64
|
+
/** Same as PUT. While PUT is used for the whole object update, PATCH is used for a partial update. */
|
|
65
|
+
export type PatchEndpoint<Body = unknown, Result = unknown> = PutEndpoint<Body, Result>;
|
|
66
|
+
/** Descriptor for DELETE routes. */
|
|
67
|
+
export type DeleteEndpoint = EndpointBase<RequestContext, void>;
|
|
68
|
+
/** Union type for all route registration info objects. */
|
|
69
|
+
export type RouteRegistrationInfo = ({
|
|
70
|
+
method: 'get';
|
|
71
|
+
route: GetEndpoint | GetListEndpoint;
|
|
72
|
+
} | {
|
|
73
|
+
method: 'post';
|
|
74
|
+
route: PostEndpoint;
|
|
75
|
+
} | {
|
|
76
|
+
method: 'patch';
|
|
77
|
+
route: PatchEndpoint;
|
|
78
|
+
} | {
|
|
79
|
+
method: 'put';
|
|
80
|
+
route: PutEndpoint;
|
|
81
|
+
} | {
|
|
82
|
+
method: 'delete';
|
|
83
|
+
route: DeleteEndpoint;
|
|
84
|
+
}) & {
|
|
85
|
+
path: string;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Registers a GET route.
|
|
89
|
+
*/
|
|
90
|
+
export declare const mountGet: (app: ExpressApplication, path: string, endpoint: GetEndpoint | GetListEndpoint) => void;
|
|
91
|
+
/**
|
|
92
|
+
* Registers a POST route.
|
|
93
|
+
*/
|
|
94
|
+
export declare const mountPost: <Body, Result>(app: ExpressApplication, path: string, endpoint: PostEndpoint<Body, Result>) => void;
|
|
95
|
+
/**
|
|
96
|
+
* Registers a PATCH route.
|
|
97
|
+
*/
|
|
98
|
+
export declare const mountPatch: <Body, Result>(app: ExpressApplication, path: string, endpoint: PatchEndpoint<Body, Result>) => void;
|
|
99
|
+
/**
|
|
100
|
+
* Registers a PUT route.
|
|
101
|
+
*/
|
|
102
|
+
export declare const mountPut: <Body, Result>(app: ExpressApplication, path: string, endpoint: PutEndpoint<Body, Result>) => void;
|
|
103
|
+
/**
|
|
104
|
+
* Registers a DELETE route.
|
|
105
|
+
*/
|
|
106
|
+
export declare const mountDelete: (app: ExpressApplication, path: string, endpoint: DeleteEndpoint) => void;
|
|
107
|
+
/**
|
|
108
|
+
* Mounts a route with the given method, endpoint, and path.
|
|
109
|
+
*/
|
|
110
|
+
export declare function mount(app: ExpressApplication, { method, route, path }: RouteRegistrationInfo): void;
|
package/dist/cjs/router.js
CHANGED
|
@@ -38,7 +38,7 @@ exports.mount = mount;
|
|
|
38
38
|
const assertions_1 = require("@fishka/assertions");
|
|
39
39
|
const url = __importStar(require("url"));
|
|
40
40
|
const api_types_1 = require("./api.types");
|
|
41
|
-
const
|
|
41
|
+
const http_status_codes_1 = require("./http-status-codes");
|
|
42
42
|
const error_handling_1 = require("./error-handling");
|
|
43
43
|
const thread_local_storage_1 = require("./thread-local/thread-local-storage");
|
|
44
44
|
const conversion_utils_1 = require("./utils/conversion.utils");
|
|
@@ -103,7 +103,7 @@ function createRouteHandler(method, endpoint) {
|
|
|
103
103
|
if (tls?.requestId) {
|
|
104
104
|
response.requestId = tls.requestId;
|
|
105
105
|
}
|
|
106
|
-
response.status = response.status ||
|
|
106
|
+
response.status = response.status || http_status_codes_1.HTTP_OK;
|
|
107
107
|
res.status(response.status);
|
|
108
108
|
res.send(response);
|
|
109
109
|
};
|
|
@@ -119,12 +119,12 @@ function validateUrlParameters(req, { $path, $query, }) {
|
|
|
119
119
|
// Run Global Validation if registered.
|
|
120
120
|
const globalValidator = api_types_1.URL_PARAMETER_INFO[key]?.validator;
|
|
121
121
|
if (globalValidator) {
|
|
122
|
-
(0, assertions_1.callValueAssertion)(value, globalValidator, `${
|
|
122
|
+
(0, assertions_1.callValueAssertion)(value, globalValidator, `${http_status_codes_1.HTTP_BAD_REQUEST}`);
|
|
123
123
|
}
|
|
124
124
|
// Run Local Validation.
|
|
125
125
|
const validator = $path?.[key];
|
|
126
126
|
if (validator) {
|
|
127
|
-
(0, assertions_1.callValueAssertion)(value, validator, `${
|
|
127
|
+
(0, assertions_1.callValueAssertion)(value, validator, `${http_status_codes_1.HTTP_BAD_REQUEST}`);
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
const parsedUrl = url.parse(req.url, true);
|
|
@@ -136,16 +136,16 @@ function validateUrlParameters(req, { $path, $query, }) {
|
|
|
136
136
|
// Query params can be string | string[] | undefined. Global validators usually expect string.
|
|
137
137
|
// We only validate if it's a single value or handle array in validator.
|
|
138
138
|
// For simplicity, we pass value as is (unknown) to assertion.
|
|
139
|
-
(0, assertions_1.callValueAssertion)(value, globalValidator, `${
|
|
139
|
+
(0, assertions_1.callValueAssertion)(value, globalValidator, `${http_status_codes_1.HTTP_BAD_REQUEST}`);
|
|
140
140
|
}
|
|
141
141
|
const validator = $query?.[key];
|
|
142
142
|
if (validator) {
|
|
143
|
-
(0, assertions_1.callValueAssertion)(value, validator, `${
|
|
143
|
+
(0, assertions_1.callValueAssertion)(value, validator, `${http_status_codes_1.HTTP_BAD_REQUEST}`);
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
catch (error) {
|
|
148
|
-
throw new api_types_1.HttpError(
|
|
148
|
+
throw new api_types_1.HttpError(http_status_codes_1.HTTP_BAD_REQUEST, (0, assertions_1.getMessageFromError)(error));
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
/**
|
|
@@ -178,23 +178,23 @@ async function executeBodyEndpoint(route, req, res) {
|
|
|
178
178
|
// Handle validation based on whether validator is an object or function
|
|
179
179
|
if (typeof validator === 'function') {
|
|
180
180
|
// It's a ValueAssertion (function)
|
|
181
|
-
(0, assertions_1.callValueAssertion)(apiRequest, validator, `${
|
|
181
|
+
(0, assertions_1.callValueAssertion)(apiRequest, validator, `${http_status_codes_1.HTTP_BAD_REQUEST}: request body`);
|
|
182
182
|
}
|
|
183
183
|
else {
|
|
184
184
|
// It's an ObjectAssertion - use validateObject
|
|
185
185
|
// We strictly assume it is an object because of the type definition (function | object)
|
|
186
186
|
const objectValidator = validator;
|
|
187
187
|
const isEmptyValidator = Object.keys(objectValidator).length === 0;
|
|
188
|
-
const
|
|
188
|
+
const errorMessage = (0, assertions_1.validateObject)(apiRequest, objectValidator, `${http_status_codes_1.HTTP_BAD_REQUEST}: request body`, {
|
|
189
189
|
failOnUnknownFields: !isEmptyValidator,
|
|
190
190
|
});
|
|
191
|
-
(0,
|
|
191
|
+
(0, api_types_1.assertHttp)(!errorMessage, http_status_codes_1.HTTP_BAD_REQUEST, errorMessage || 'Request body validation failed');
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
catch (error) {
|
|
195
195
|
if (error instanceof api_types_1.HttpError)
|
|
196
196
|
throw error;
|
|
197
|
-
throw new api_types_1.HttpError(
|
|
197
|
+
throw new api_types_1.HttpError(http_status_codes_1.HTTP_BAD_REQUEST, (0, assertions_1.getMessageFromError)(error));
|
|
198
198
|
}
|
|
199
199
|
const requestContext = newRequestContext(apiRequest, req, res);
|
|
200
200
|
validateUrlParameters(req, { $path: route.$path, $query: route.$query });
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ExpressFunction } from '../utils/express.utils';
|
|
2
|
+
/**
|
|
3
|
+
* Creates middleware that initializes thread-local storage for each request.
|
|
4
|
+
* Automatically generates a unique request ID and makes it available throughout
|
|
5
|
+
* the request lifecycle.
|
|
6
|
+
*
|
|
7
|
+
* @returns Express middleware function
|
|
8
|
+
*/
|
|
9
|
+
export declare function createTlsMiddleware(): ExpressFunction;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thread-local storage data for per-request context.
|
|
3
|
+
* Stores information that should be available throughout the request lifecycle.
|
|
4
|
+
*/
|
|
5
|
+
export interface ThreadLocalData {
|
|
6
|
+
/** Unique request identifier */
|
|
7
|
+
requestId: string;
|
|
8
|
+
/** Additional custom fields can be stored */
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Gets all thread-local data for the current request context.
|
|
13
|
+
* Returns undefined if called outside an async context managed by API.
|
|
14
|
+
* @Internal
|
|
15
|
+
*/
|
|
16
|
+
export declare function getRequestLocalStorage(): ThreadLocalData | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Executes a callback within a request context with the given thread-local data.
|
|
19
|
+
* Used by middleware to set up the context for handlers.
|
|
20
|
+
* @Internal
|
|
21
|
+
* @param data - Thread-local data to establish
|
|
22
|
+
* @param callback - Function to execute within the context
|
|
23
|
+
* @returns Result of the callback
|
|
24
|
+
*/
|
|
25
|
+
export declare function runWithRequestTlsData<T>(data: ThreadLocalData, callback: () => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ApiResponse } from '../api.types';
|
|
2
|
+
/**
|
|
3
|
+
* Converts JS timestamp or date to ISO 8601 format (without milliseconds).
|
|
4
|
+
* Example: "2012-07-20T01:19:13Z".
|
|
5
|
+
* @Internal
|
|
6
|
+
*/
|
|
7
|
+
export declare function toApiDateString(value: number | Date): string;
|
|
8
|
+
/**
|
|
9
|
+
* Wraps the response into the correct API form.
|
|
10
|
+
* Add necessary fields, like 'requestId'.
|
|
11
|
+
* If the response is already in the correct form, returns it as-is.
|
|
12
|
+
* @Internal
|
|
13
|
+
*/
|
|
14
|
+
export declare function wrapAsApiResponse<T = unknown>(apiResponseOrResultValue: T | ApiResponse<T>): ApiResponse<T>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
/** Shortcut for express.* types with no namespace: so the type can be found/imported by IDE. */
|
|
3
|
+
export type ExpressRequest = express.Request;
|
|
4
|
+
export type ExpressResponse = express.Response;
|
|
5
|
+
export type ExpressNextFunction = express.NextFunction;
|
|
6
|
+
export type ExpressApplication = express.Application;
|
|
7
|
+
export type ExpressFunction = (req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => Promise<void>;
|