@coherent.js/api 1.0.0-beta.3 → 1.0.0-beta.6
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/index.cjs +180 -52
- package/dist/index.cjs.map +3 -3
- package/dist/index.js +180 -52
- package/dist/index.js.map +3 -3
- package/package.json +26 -2
- package/types/index.d.ts +188 -38
- package/dist/api/errors.d.ts +0 -92
- package/dist/api/errors.d.ts.map +0 -1
- package/dist/api/errors.js +0 -161
- package/dist/api/errors.js.map +0 -1
- package/dist/api/index.d.ts +0 -61
- package/dist/api/index.d.ts.map +0 -1
- package/dist/api/index.js +0 -41
- package/dist/api/index.js.map +0 -1
- package/dist/api/middleware.d.ts +0 -57
- package/dist/api/middleware.d.ts.map +0 -1
- package/dist/api/middleware.js +0 -244
- package/dist/api/middleware.js.map +0 -1
- package/dist/api/openapi.d.ts +0 -54
- package/dist/api/openapi.d.ts.map +0 -1
- package/dist/api/openapi.js +0 -144
- package/dist/api/openapi.js.map +0 -1
- package/dist/api/router.d.ts +0 -30
- package/dist/api/router.d.ts.map +0 -1
- package/dist/api/router.js +0 -1476
- package/dist/api/router.js.map +0 -1
- package/dist/api/security.d.ts +0 -64
- package/dist/api/security.d.ts.map +0 -1
- package/dist/api/security.js +0 -239
- package/dist/api/security.js.map +0 -1
- package/dist/api/serialization.d.ts +0 -86
- package/dist/api/serialization.d.ts.map +0 -1
- package/dist/api/serialization.js +0 -151
- package/dist/api/serialization.js.map +0 -1
- package/dist/api/validation.d.ts +0 -34
- package/dist/api/validation.d.ts.map +0 -1
- package/dist/api/validation.js +0 -172
- package/dist/api/validation.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -56,6 +56,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
56
56
|
var import_node_crypto = require("node:crypto");
|
|
57
57
|
|
|
58
58
|
// src/errors.js
|
|
59
|
+
var import_node_process = require("node:process");
|
|
59
60
|
var ApiError = class _ApiError extends Error {
|
|
60
61
|
/**
|
|
61
62
|
* Create an API _error
|
|
@@ -162,7 +163,7 @@ function createErrorHandler() {
|
|
|
162
163
|
if (_error.details) {
|
|
163
164
|
response.details = _error.details;
|
|
164
165
|
}
|
|
165
|
-
if (
|
|
166
|
+
if (import_node_process.env.NODE_ENV === "development") {
|
|
166
167
|
response.stack = _error.stack;
|
|
167
168
|
}
|
|
168
169
|
res.status(response.statusCode).json(response);
|
|
@@ -194,8 +195,10 @@ function validateAgainstSchema(schema, data) {
|
|
|
194
195
|
for (const [field, fieldSchema] of Object.entries(schema.properties)) {
|
|
195
196
|
if (field in data) {
|
|
196
197
|
const fieldValue = data[field];
|
|
197
|
-
const
|
|
198
|
-
|
|
198
|
+
const fieldResult = validateField(fieldSchema, fieldValue, field);
|
|
199
|
+
if (!fieldResult.valid) {
|
|
200
|
+
errors.push(...fieldResult.errors);
|
|
201
|
+
}
|
|
199
202
|
}
|
|
200
203
|
}
|
|
201
204
|
}
|
|
@@ -207,6 +210,15 @@ function validateAgainstSchema(schema, data) {
|
|
|
207
210
|
}
|
|
208
211
|
function validateField(schema, value, fieldName) {
|
|
209
212
|
const errors = [];
|
|
213
|
+
if (value === null || value === void 0) {
|
|
214
|
+
if (schema.type && schema.type !== "null") {
|
|
215
|
+
errors.push({
|
|
216
|
+
field: fieldName,
|
|
217
|
+
message: `Expected ${schema.type}, got ${value === null ? "null" : "undefined"}`
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
return { valid: errors.length === 0, errors };
|
|
221
|
+
}
|
|
210
222
|
if (schema.type) {
|
|
211
223
|
if (schema.type === "string" && typeof value !== "string") {
|
|
212
224
|
errors.push({
|
|
@@ -230,7 +242,7 @@ function validateField(schema, value, fieldName) {
|
|
|
230
242
|
});
|
|
231
243
|
}
|
|
232
244
|
}
|
|
233
|
-
if (schema.type === "string") {
|
|
245
|
+
if (schema.type === "string" && typeof value === "string") {
|
|
234
246
|
if (schema.minLength && value.length < schema.minLength) {
|
|
235
247
|
errors.push({
|
|
236
248
|
field: fieldName,
|
|
@@ -250,7 +262,7 @@ function validateField(schema, value, fieldName) {
|
|
|
250
262
|
});
|
|
251
263
|
}
|
|
252
264
|
}
|
|
253
|
-
if (schema.type === "number") {
|
|
265
|
+
if (schema.type === "number" && typeof value === "number") {
|
|
254
266
|
if (schema.minimum !== void 0 && value < schema.minimum) {
|
|
255
267
|
errors.push({
|
|
256
268
|
field: fieldName,
|
|
@@ -264,7 +276,7 @@ function validateField(schema, value, fieldName) {
|
|
|
264
276
|
});
|
|
265
277
|
}
|
|
266
278
|
}
|
|
267
|
-
return errors;
|
|
279
|
+
return { valid: errors.length === 0, errors };
|
|
268
280
|
}
|
|
269
281
|
function withValidation(schema) {
|
|
270
282
|
return (req, res, next) => {
|
|
@@ -494,6 +506,9 @@ function registerRoute(method, config, router, path) {
|
|
|
494
506
|
}
|
|
495
507
|
}, { name });
|
|
496
508
|
}
|
|
509
|
+
function isStaticRoute(pattern) {
|
|
510
|
+
return !pattern.includes(":") && !pattern.includes("*") && !pattern.includes("(") && !pattern.includes("?");
|
|
511
|
+
}
|
|
497
512
|
var SimpleRouter = class {
|
|
498
513
|
constructor(options = {}) {
|
|
499
514
|
this.routes = [];
|
|
@@ -505,6 +520,7 @@ var SimpleRouter = class {
|
|
|
505
520
|
this.enableCompilation = options.enableCompilation !== false;
|
|
506
521
|
this.compiledRoutes = /* @__PURE__ */ new Map();
|
|
507
522
|
this.routeCompilationCache = /* @__PURE__ */ new Map();
|
|
523
|
+
this.maxCompilationCacheSize = options.maxCompilationCacheSize || 1e3;
|
|
508
524
|
this.enableVersioning = options.enableVersioning || false;
|
|
509
525
|
this.defaultVersion = options.defaultVersion || "v1";
|
|
510
526
|
this.versionHeader = options.versionHeader || "api-version";
|
|
@@ -533,10 +549,15 @@ var SimpleRouter = class {
|
|
|
533
549
|
// Track WebSocket messages
|
|
534
550
|
};
|
|
535
551
|
}
|
|
552
|
+
this.enableSecurityHeaders = options.enableSecurityHeaders !== false;
|
|
553
|
+
this.enableCORS = options.enableCORS !== false;
|
|
554
|
+
this.enableSmartRouting = options.enableSmartRouting !== false;
|
|
555
|
+
this.staticRoutes = /* @__PURE__ */ new Map();
|
|
556
|
+
this.enableRouteMetrics = options.enableRouteMetrics || false;
|
|
536
557
|
}
|
|
537
558
|
/**
|
|
538
559
|
* Add an HTTP route to the router
|
|
539
|
-
*
|
|
560
|
+
*
|
|
540
561
|
* @param {string} method - HTTP method (GET, POST, PUT, DELETE, PATCH)
|
|
541
562
|
* @param {string} path - Route path pattern (supports :param and wildcards)
|
|
542
563
|
* @param {Function} handler - Route handler function
|
|
@@ -544,7 +565,7 @@ var SimpleRouter = class {
|
|
|
544
565
|
* @param {Array} [options.middleware] - Route-specific middleware
|
|
545
566
|
* @param {string} [options.name] - Named route for URL generation
|
|
546
567
|
* @param {string} [options.version] - API version for this route
|
|
547
|
-
*
|
|
568
|
+
*
|
|
548
569
|
* @example
|
|
549
570
|
* router.addRoute('GET', '/users/:id', (req, res) => {
|
|
550
571
|
* return { user: { id: req.params.id } };
|
|
@@ -567,6 +588,11 @@ var SimpleRouter = class {
|
|
|
567
588
|
if (this.enableCompilation) {
|
|
568
589
|
route.compiled = this.compileRoute(fullPath);
|
|
569
590
|
}
|
|
591
|
+
if (this.enableSmartRouting && isStaticRoute(fullPath)) {
|
|
592
|
+
const staticKey = `${route.method}:${fullPath}`;
|
|
593
|
+
this.staticRoutes.set(staticKey, route);
|
|
594
|
+
route.isStatic = true;
|
|
595
|
+
}
|
|
570
596
|
this.routes.push(route);
|
|
571
597
|
if (this.enableVersioning) {
|
|
572
598
|
if (!this.versionedRoutes.has(route.version)) {
|
|
@@ -880,7 +906,7 @@ var SimpleRouter = class {
|
|
|
880
906
|
}
|
|
881
907
|
});
|
|
882
908
|
socket.on("_error", (err) => {
|
|
883
|
-
console.
|
|
909
|
+
console.error("WebSocket socket _error (connection likely closed):", err.code);
|
|
884
910
|
});
|
|
885
911
|
return ws;
|
|
886
912
|
}
|
|
@@ -939,7 +965,7 @@ var SimpleRouter = class {
|
|
|
939
965
|
try {
|
|
940
966
|
ws.send(message);
|
|
941
967
|
} catch {
|
|
942
|
-
console.
|
|
968
|
+
console.error("Failed to send message to connection:", id);
|
|
943
969
|
}
|
|
944
970
|
}
|
|
945
971
|
}
|
|
@@ -977,16 +1003,16 @@ var SimpleRouter = class {
|
|
|
977
1003
|
}
|
|
978
1004
|
/**
|
|
979
1005
|
* Generate URL for named route with parameter substitution
|
|
980
|
-
*
|
|
1006
|
+
*
|
|
981
1007
|
* @param {string} name - Route name (set during route registration)
|
|
982
1008
|
* @param {Object} [params={}] - Parameters to substitute in the URL pattern
|
|
983
1009
|
* @returns {string} Generated URL with parameters substituted
|
|
984
1010
|
* @throws {Error} If named route is not found
|
|
985
|
-
*
|
|
1011
|
+
*
|
|
986
1012
|
* @example
|
|
987
1013
|
* // Route registered as: router.addRoute('GET', '/users/:id', handler, { name: 'getUser' })
|
|
988
1014
|
* const url = router.url('getUser', { id: 123 }); // '/users/123'
|
|
989
|
-
*
|
|
1015
|
+
*
|
|
990
1016
|
* // With constrained parameters
|
|
991
1017
|
* const url = router.url('getUserPosts', { userId: 123, postId: 456 }); // '/users/123/posts/456'
|
|
992
1018
|
*/
|
|
@@ -1004,11 +1030,11 @@ var SimpleRouter = class {
|
|
|
1004
1030
|
}
|
|
1005
1031
|
/**
|
|
1006
1032
|
* Add routes from configuration object
|
|
1007
|
-
*
|
|
1033
|
+
*
|
|
1008
1034
|
* @param {Object} routeConfig - Route configuration object with nested structure
|
|
1009
1035
|
* @description Processes nested route objects and registers HTTP and WebSocket routes.
|
|
1010
1036
|
* Supports declarative route definition with automatic method detection.
|
|
1011
|
-
*
|
|
1037
|
+
*
|
|
1012
1038
|
* @example
|
|
1013
1039
|
* router.addRoutes({
|
|
1014
1040
|
* 'api': {
|
|
@@ -1024,17 +1050,17 @@ var SimpleRouter = class {
|
|
|
1024
1050
|
}
|
|
1025
1051
|
/**
|
|
1026
1052
|
* Add global middleware to the router
|
|
1027
|
-
*
|
|
1053
|
+
*
|
|
1028
1054
|
* @param {Function|Object} middleware - Middleware function or conditional middleware object
|
|
1029
1055
|
* @description Adds middleware that runs before all route handlers. Supports both
|
|
1030
1056
|
* simple functions and conditional middleware objects.
|
|
1031
|
-
*
|
|
1057
|
+
*
|
|
1032
1058
|
* @example
|
|
1033
1059
|
* // Simple middleware
|
|
1034
1060
|
* router.use((req, res) => {
|
|
1035
1061
|
* console.log(`${req.method} ${req.url}`);
|
|
1036
1062
|
* });
|
|
1037
|
-
*
|
|
1063
|
+
*
|
|
1038
1064
|
* // Conditional middleware
|
|
1039
1065
|
* router.use({
|
|
1040
1066
|
* condition: (req) => req.url.startsWith('/api'),
|
|
@@ -1164,7 +1190,10 @@ var SimpleRouter = class {
|
|
|
1164
1190
|
compileRoute(pattern) {
|
|
1165
1191
|
if (this.routeCompilationCache.has(pattern)) {
|
|
1166
1192
|
if (this.enableMetrics) this.metrics.compilationHits++;
|
|
1167
|
-
|
|
1193
|
+
const compiled2 = this.routeCompilationCache.get(pattern);
|
|
1194
|
+
this.routeCompilationCache.delete(pattern);
|
|
1195
|
+
this.routeCompilationCache.set(pattern, compiled2);
|
|
1196
|
+
return compiled2;
|
|
1168
1197
|
}
|
|
1169
1198
|
const paramNames = [];
|
|
1170
1199
|
let regexPattern = pattern;
|
|
@@ -1175,25 +1204,38 @@ var SimpleRouter = class {
|
|
|
1175
1204
|
regexPattern = regexPattern.replace(/\/\*/g, "/([^/]+)");
|
|
1176
1205
|
paramNames.push("splat");
|
|
1177
1206
|
}
|
|
1178
|
-
regexPattern = regexPattern.replace(/:([^(/]+)(\([^)]+\))?(\?)?/g, (match, paramName, constraint, optional) => {
|
|
1207
|
+
regexPattern = regexPattern.replace(/:([^(/]+)(\([^)]+\))?(\?)?/g, (match, paramName, constraint, optional, offset, fullString) => {
|
|
1179
1208
|
paramNames.push(paramName);
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1209
|
+
const hasPrecedingSlash = offset > 0 && fullString[offset - 1] === "/";
|
|
1210
|
+
if (hasPrecedingSlash) {
|
|
1211
|
+
if (constraint) {
|
|
1212
|
+
const constraintPattern = constraint.slice(1, -1);
|
|
1213
|
+
return optional ? `(?:/(?:${constraintPattern}))?` : `(${constraintPattern})`;
|
|
1214
|
+
} else {
|
|
1215
|
+
return optional ? `(?:([^/]+))?` : `([^/]+)`;
|
|
1216
|
+
}
|
|
1183
1217
|
} else {
|
|
1184
|
-
|
|
1218
|
+
if (constraint) {
|
|
1219
|
+
const constraintPattern = constraint.slice(1, -1);
|
|
1220
|
+
return optional ? `(?:/(?:${constraintPattern}))?` : `/${constraintPattern}`;
|
|
1221
|
+
} else {
|
|
1222
|
+
return optional ? `(?:/([^/]+))?` : `/([^/]+)`;
|
|
1223
|
+
}
|
|
1185
1224
|
}
|
|
1186
1225
|
});
|
|
1187
|
-
regexPattern = regexPattern.replace(/[.+?^${}
|
|
1226
|
+
regexPattern = regexPattern.replace(/([.+?^${}|\\[\]()]])/g, "\\$1");
|
|
1227
|
+
regexPattern = regexPattern.replace(/\\\[/g, "[").replace(/\\\]/g, "]").replace(/\\\^/g, "^");
|
|
1188
1228
|
regexPattern = `^${regexPattern}$`;
|
|
1189
1229
|
const compiled = {
|
|
1190
1230
|
regex: new RegExp(regexPattern),
|
|
1191
1231
|
paramNames,
|
|
1192
1232
|
pattern
|
|
1193
1233
|
};
|
|
1194
|
-
if (this.routeCompilationCache.size
|
|
1195
|
-
this.routeCompilationCache.
|
|
1234
|
+
if (this.routeCompilationCache.size >= this.maxCompilationCacheSize) {
|
|
1235
|
+
const firstKey = this.routeCompilationCache.keys().next().value;
|
|
1236
|
+
this.routeCompilationCache.delete(firstKey);
|
|
1196
1237
|
}
|
|
1238
|
+
this.routeCompilationCache.set(pattern, compiled);
|
|
1197
1239
|
return compiled;
|
|
1198
1240
|
}
|
|
1199
1241
|
/**
|
|
@@ -1369,7 +1411,15 @@ var SimpleRouter = class {
|
|
|
1369
1411
|
this.metrics.requests++;
|
|
1370
1412
|
}
|
|
1371
1413
|
const { corsOrigin, rateLimit = { windowMs: 6e4, maxRequests: 100 } } = options;
|
|
1372
|
-
|
|
1414
|
+
if (this.enableSecurityHeaders) {
|
|
1415
|
+
addSecurityHeaders(res, corsOrigin);
|
|
1416
|
+
} else if (this.enableCORS) {
|
|
1417
|
+
const origin = corsOrigin || "http://localhost:3000";
|
|
1418
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
1419
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
|
|
1420
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
1421
|
+
res.setHeader("Access-Control-Allow-Credentials", "true");
|
|
1422
|
+
}
|
|
1373
1423
|
if (req.method === "OPTIONS") {
|
|
1374
1424
|
res.writeHead(204);
|
|
1375
1425
|
res.end();
|
|
@@ -1383,7 +1433,9 @@ var SimpleRouter = class {
|
|
|
1383
1433
|
}
|
|
1384
1434
|
const parsedUrl = (0, import_node_url.parse)(req.url, true);
|
|
1385
1435
|
const pathname = parsedUrl.pathname;
|
|
1386
|
-
req.query
|
|
1436
|
+
if (!req.query) {
|
|
1437
|
+
req.query = parsedUrl.query || {};
|
|
1438
|
+
}
|
|
1387
1439
|
try {
|
|
1388
1440
|
req.body = await parseBody(req, options.maxBodySize);
|
|
1389
1441
|
} catch (_error) {
|
|
@@ -1403,24 +1455,48 @@ var SimpleRouter = class {
|
|
|
1403
1455
|
if (this.enableMetrics && requestVersion) {
|
|
1404
1456
|
this.metrics.versionRequests.set(requestVersion, (this.metrics.versionRequests.get(requestVersion) || 0) + 1);
|
|
1405
1457
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1458
|
+
matchedRoute = null;
|
|
1459
|
+
if (this.enableSmartRouting) {
|
|
1460
|
+
const staticKey = `${req.method}:${pathname}`;
|
|
1461
|
+
const staticRoute = this.staticRoutes.get(staticKey);
|
|
1462
|
+
if (staticRoute) {
|
|
1463
|
+
if (!this.enableVersioning || staticRoute.version === requestVersion) {
|
|
1464
|
+
matchedRoute = { route: staticRoute, params: {} };
|
|
1465
|
+
if (this.enableRouteMetrics && this.enableMetrics) {
|
|
1466
|
+
if (!this.metrics.staticRouteMatches) {
|
|
1467
|
+
this.metrics.staticRouteMatches = 0;
|
|
1468
|
+
}
|
|
1469
|
+
this.metrics.staticRouteMatches++;
|
|
1470
|
+
}
|
|
1417
1471
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
if (!matchedRoute) {
|
|
1475
|
+
const routesToSearch = this.enableVersioning && this.versionedRoutes.has(requestVersion) ? this.versionedRoutes.get(requestVersion) : this.routes;
|
|
1476
|
+
for (const route of routesToSearch) {
|
|
1477
|
+
if (route.method === req.method) {
|
|
1478
|
+
if (this.enableVersioning && route.version !== requestVersion) {
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1481
|
+
let params = null;
|
|
1482
|
+
if (this.enableCompilation && route.compiled) {
|
|
1483
|
+
params = this.matchCompiledRoute(route.compiled, pathname);
|
|
1484
|
+
} else {
|
|
1485
|
+
params = extractParams(route.path, pathname);
|
|
1486
|
+
}
|
|
1487
|
+
if (params !== null) {
|
|
1488
|
+
matchedRoute = { route, params };
|
|
1489
|
+
if (this.enableRouteMetrics && this.enableMetrics) {
|
|
1490
|
+
if (!this.metrics.dynamicRouteMatches) {
|
|
1491
|
+
this.metrics.dynamicRouteMatches = 0;
|
|
1492
|
+
}
|
|
1493
|
+
this.metrics.dynamicRouteMatches++;
|
|
1494
|
+
}
|
|
1495
|
+
if (this.routeCache.size < this.maxCacheSize) {
|
|
1496
|
+
this.routeCache.set(cacheKey, matchedRoute);
|
|
1497
|
+
}
|
|
1498
|
+
break;
|
|
1422
1499
|
}
|
|
1423
|
-
break;
|
|
1424
1500
|
}
|
|
1425
1501
|
}
|
|
1426
1502
|
}
|
|
@@ -1431,7 +1507,6 @@ var SimpleRouter = class {
|
|
|
1431
1507
|
const routeKey = `${req.method}:${matchedRoute.route.path}`;
|
|
1432
1508
|
this.metrics.routeMatches.set(routeKey, (this.metrics.routeMatches.get(routeKey) || 0) + 1);
|
|
1433
1509
|
}
|
|
1434
|
-
console.log(`${(/* @__PURE__ */ new Date()).toISOString()} ${req.method} ${pathname}`);
|
|
1435
1510
|
try {
|
|
1436
1511
|
if (matchedRoute.route.middleware && matchedRoute.route.middleware.length > 0) {
|
|
1437
1512
|
for (const middleware of matchedRoute.route.middleware) {
|
|
@@ -1441,16 +1516,26 @@ var SimpleRouter = class {
|
|
|
1441
1516
|
}
|
|
1442
1517
|
const { route } = matchedRoute;
|
|
1443
1518
|
const result = await route.handler(req, res);
|
|
1444
|
-
if (result &&
|
|
1445
|
-
|
|
1446
|
-
|
|
1519
|
+
if (result && !res.headersSent) {
|
|
1520
|
+
if (typeof result === "object") {
|
|
1521
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1522
|
+
res.end(JSON.stringify(result));
|
|
1523
|
+
} else if (typeof result === "string") {
|
|
1524
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
1525
|
+
res.end(result);
|
|
1526
|
+
}
|
|
1447
1527
|
}
|
|
1448
1528
|
if (this.enableMetrics) {
|
|
1449
1529
|
const responseTime = Date.now() - startTime;
|
|
1450
|
-
this.
|
|
1451
|
-
|
|
1452
|
-
this.metrics.responseTime = this.metrics.responseTime.slice(-1e3);
|
|
1530
|
+
if (!this._metricsLock) {
|
|
1531
|
+
this._metricsLock = Promise.resolve();
|
|
1453
1532
|
}
|
|
1533
|
+
this._metricsLock = this._metricsLock.then(() => {
|
|
1534
|
+
this.metrics.responseTime.push(responseTime);
|
|
1535
|
+
if (this.metrics.responseTime.length > 1e3) {
|
|
1536
|
+
this.metrics.responseTime = this.metrics.responseTime.slice(-1e3);
|
|
1537
|
+
}
|
|
1538
|
+
});
|
|
1454
1539
|
}
|
|
1455
1540
|
return;
|
|
1456
1541
|
} catch (_error) {
|
|
@@ -1494,6 +1579,49 @@ var SimpleRouter = class {
|
|
|
1494
1579
|
head(path, handler, options = {}) {
|
|
1495
1580
|
return this.addRoute("HEAD", path, handler, options);
|
|
1496
1581
|
}
|
|
1582
|
+
/**
|
|
1583
|
+
* Convert to Express Router middleware
|
|
1584
|
+
*
|
|
1585
|
+
* @param {Object} express - Express module (required)
|
|
1586
|
+
* @returns {Function} Express-compatible router middleware
|
|
1587
|
+
*
|
|
1588
|
+
* @example
|
|
1589
|
+
* import express from 'express';
|
|
1590
|
+
* const router = createRouter();
|
|
1591
|
+
* router.get('/users', handler);
|
|
1592
|
+
* app.use('/api', router.toExpressRouter(express));
|
|
1593
|
+
*/
|
|
1594
|
+
toExpressRouter(express) {
|
|
1595
|
+
if (!express || typeof express.Router !== "function") {
|
|
1596
|
+
throw new Error("Express is required for toExpressRouter(). Pass the express module as argument: router.toExpressRouter(express)");
|
|
1597
|
+
}
|
|
1598
|
+
const expressRouter = express.Router();
|
|
1599
|
+
for (const route of this.routes) {
|
|
1600
|
+
const method = route.method.toLowerCase();
|
|
1601
|
+
const path = route.path;
|
|
1602
|
+
const handler = route.handler;
|
|
1603
|
+
const middleware = route.middleware || [];
|
|
1604
|
+
const expressHandler = async (req, res, next) => {
|
|
1605
|
+
try {
|
|
1606
|
+
for (const mw of middleware) {
|
|
1607
|
+
await new Promise((resolve, reject) => {
|
|
1608
|
+
mw(req, res, (err) => err ? reject(err) : resolve());
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
const result = await handler(req, res);
|
|
1612
|
+
if (result !== void 0 && !res.headersSent) {
|
|
1613
|
+
res.json(result);
|
|
1614
|
+
}
|
|
1615
|
+
} catch (error) {
|
|
1616
|
+
next(error);
|
|
1617
|
+
}
|
|
1618
|
+
};
|
|
1619
|
+
if (typeof expressRouter[method] === "function") {
|
|
1620
|
+
expressRouter[method](path, expressHandler);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
return expressRouter;
|
|
1624
|
+
}
|
|
1497
1625
|
};
|
|
1498
1626
|
function createRouter(routeConfig, options = {}) {
|
|
1499
1627
|
const router = new SimpleRouter(options);
|