@weconjs/core 0.2.0 → 1.2.0
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/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +6 -8
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +30 -21
- package/dist/context.js.map +1 -1
- package/dist/database/index.d.ts.map +1 -1
- package/dist/database/index.js +1 -2
- package/dist/database/index.js.map +1 -1
- package/dist/devtools/client/assets/ModuleDetail-Cv1SUhav.js +1 -0
- package/dist/devtools/client/assets/TranslationEditor-CwBjcmjN.js +6 -0
- package/dist/devtools/client/assets/api-DHFLRYt1.js +11 -0
- package/dist/devtools/client/assets/arrow-left-XXd8AsXj.js +6 -0
- package/dist/devtools/client/assets/chevron-right-DN23SEYa.js +6 -0
- package/dist/devtools/client/assets/index-BFzJyDrn.css +1 -0
- package/dist/devtools/client/assets/index-BbARU3f6.js +1 -0
- package/dist/devtools/client/assets/index-Cwwdi5uz.js +181 -0
- package/dist/devtools/client/assets/index-DH5TZlGZ.js +6 -0
- package/dist/devtools/client/assets/index-DbBxhrHS.js +1 -0
- package/dist/devtools/client/assets/index-LkCf2mcb.js +1 -0
- package/dist/devtools/client/assets/index-jqJ0mfFs.js +6 -0
- package/dist/devtools/client/index.html +15 -0
- package/dist/devtools/controllers/config.controller.d.ts +28 -0
- package/dist/devtools/controllers/config.controller.d.ts.map +1 -0
- package/dist/devtools/controllers/config.controller.js +100 -0
- package/dist/devtools/controllers/config.controller.js.map +1 -0
- package/dist/devtools/controllers/i18n.controller.d.ts +22 -0
- package/dist/devtools/controllers/i18n.controller.d.ts.map +1 -0
- package/dist/devtools/controllers/i18n.controller.js +93 -0
- package/dist/devtools/controllers/i18n.controller.js.map +1 -0
- package/dist/devtools/controllers/modules.controller.d.ts +16 -0
- package/dist/devtools/controllers/modules.controller.d.ts.map +1 -0
- package/dist/devtools/controllers/modules.controller.js +73 -0
- package/dist/devtools/controllers/modules.controller.js.map +1 -0
- package/dist/devtools/controllers/routes.controller.d.ts +13 -0
- package/dist/devtools/controllers/routes.controller.d.ts.map +1 -0
- package/dist/devtools/controllers/routes.controller.js +32 -0
- package/dist/devtools/controllers/routes.controller.js.map +1 -0
- package/dist/devtools/index.d.ts +29 -0
- package/dist/devtools/index.d.ts.map +1 -0
- package/dist/devtools/index.js +94 -0
- package/dist/devtools/index.js.map +1 -0
- package/dist/devtools/middleware.d.ts +11 -0
- package/dist/devtools/middleware.d.ts.map +1 -0
- package/dist/devtools/middleware.js +30 -0
- package/dist/devtools/middleware.js.map +1 -0
- package/dist/devtools/types.d.ts +59 -0
- package/dist/devtools/types.d.ts.map +1 -0
- package/dist/devtools/types.js +5 -0
- package/dist/devtools/types.js.map +1 -0
- package/dist/errors/ConfigError.d.ts +6 -0
- package/dist/errors/ConfigError.d.ts.map +1 -0
- package/dist/errors/ConfigError.js +16 -0
- package/dist/errors/ConfigError.js.map +1 -0
- package/dist/errors/RequestError.d.ts +6 -0
- package/dist/errors/RequestError.d.ts.map +1 -0
- package/dist/errors/RequestError.js +16 -0
- package/dist/errors/RequestError.js.map +1 -0
- package/dist/errors/index.d.ts +9 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +5 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/module/index.d.ts +6 -0
- package/dist/module/index.d.ts.map +1 -0
- package/dist/module/index.js +5 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/module-loader.d.ts +60 -0
- package/dist/module/module-loader.d.ts.map +1 -0
- package/dist/module/module-loader.js +119 -0
- package/dist/module/module-loader.js.map +1 -0
- package/dist/routing/ErrorCatcher.d.ts +18 -0
- package/dist/routing/ErrorCatcher.d.ts.map +1 -0
- package/dist/routing/ErrorCatcher.js +50 -0
- package/dist/routing/ErrorCatcher.js.map +1 -0
- package/dist/routing/RaiMatcher.d.ts +27 -0
- package/dist/routing/RaiMatcher.d.ts.map +1 -0
- package/dist/routing/RaiMatcher.js +127 -0
- package/dist/routing/RaiMatcher.js.map +1 -0
- package/dist/routing/Route.d.ts +28 -0
- package/dist/routing/Route.d.ts.map +1 -0
- package/dist/routing/Route.js +125 -0
- package/dist/routing/Route.js.map +1 -0
- package/dist/routing/Routes.d.ts +31 -0
- package/dist/routing/Routes.d.ts.map +1 -0
- package/dist/routing/Routes.js +151 -0
- package/dist/routing/Routes.js.map +1 -0
- package/dist/routing/RoutesParam.d.ts +29 -0
- package/dist/routing/RoutesParam.d.ts.map +1 -0
- package/dist/routing/RoutesParam.js +113 -0
- package/dist/routing/RoutesParam.js.map +1 -0
- package/dist/routing/Wecon.d.ts +66 -0
- package/dist/routing/Wecon.d.ts.map +1 -0
- package/dist/routing/Wecon.js +248 -0
- package/dist/routing/Wecon.js.map +1 -0
- package/dist/routing/index.d.ts +9 -0
- package/dist/routing/index.d.ts.map +1 -0
- package/dist/routing/index.js +7 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/server/banner.d.ts +20 -0
- package/dist/server/banner.d.ts.map +1 -0
- package/dist/server/banner.js +77 -0
- package/dist/server/banner.js.map +1 -0
- package/dist/server/index.d.ts +29 -5
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +76 -12
- package/dist/server/index.js.map +1 -1
- package/dist/types.d.ts +75 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { match } from "path-to-regexp";
|
|
2
|
+
import errors from "../errors/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* High-performance RAI lookup engine.
|
|
5
|
+
* Pre-compiles path matchers, uses exact + runtime caches for O(1) lookups.
|
|
6
|
+
*/
|
|
7
|
+
class RaiMatcher {
|
|
8
|
+
cache;
|
|
9
|
+
routesByMethod;
|
|
10
|
+
exactRoutes;
|
|
11
|
+
constructor(raisList) {
|
|
12
|
+
this.cache = new Map();
|
|
13
|
+
this.routesByMethod = new Map();
|
|
14
|
+
this.exactRoutes = new Map();
|
|
15
|
+
this.initialize(raisList);
|
|
16
|
+
}
|
|
17
|
+
/** Pre-compile all route matchers and organize by HTTP method */
|
|
18
|
+
initialize(raisList) {
|
|
19
|
+
for (const route of raisList) {
|
|
20
|
+
if (typeof route.path !== "string" || !route.method) {
|
|
21
|
+
console.warn(`Invalid route configuration:`, route);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const normalizedMethod = route.method.trim().toUpperCase();
|
|
25
|
+
// Cache exact routes (no params) for O(1) lookup
|
|
26
|
+
if (!route.path.includes(":") && !route.path.includes("*")) {
|
|
27
|
+
const exactKey = `${normalizedMethod}:${route.path}`;
|
|
28
|
+
this.exactRoutes.set(exactKey, route.rai);
|
|
29
|
+
// Also handle trailing slash variant
|
|
30
|
+
const withSlash = route.path.endsWith("/")
|
|
31
|
+
? route.path.slice(0, -1)
|
|
32
|
+
: route.path + "/";
|
|
33
|
+
this.exactRoutes.set(`${normalizedMethod}:${withSlash}`, route.rai);
|
|
34
|
+
}
|
|
35
|
+
const cachedRoute = {
|
|
36
|
+
matcher: match(route.path, { decode: decodeURIComponent }),
|
|
37
|
+
rai: route.rai,
|
|
38
|
+
method: normalizedMethod,
|
|
39
|
+
path: route.path,
|
|
40
|
+
};
|
|
41
|
+
if (!this.routesByMethod.has(normalizedMethod)) {
|
|
42
|
+
this.routesByMethod.set(normalizedMethod, []);
|
|
43
|
+
}
|
|
44
|
+
this.routesByMethod.get(normalizedMethod).push(cachedRoute);
|
|
45
|
+
}
|
|
46
|
+
// Sort by specificity (static segments score higher)
|
|
47
|
+
for (const [, routes] of this.routesByMethod) {
|
|
48
|
+
routes.sort((a, b) => this.getRouteSpecificity(b.path) - this.getRouteSpecificity(a.path));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Score route specificity: static=10, dynamic=1, wildcard=0.5 */
|
|
52
|
+
getRouteSpecificity(path) {
|
|
53
|
+
let score = 0;
|
|
54
|
+
const segments = path.split("/").filter(Boolean);
|
|
55
|
+
for (let i = 0; i < segments.length; i++) {
|
|
56
|
+
const segment = segments[i];
|
|
57
|
+
if (segment.startsWith(":")) {
|
|
58
|
+
score += 1;
|
|
59
|
+
}
|
|
60
|
+
else if (segment === "*") {
|
|
61
|
+
score += 0.5;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
score += 10;
|
|
65
|
+
}
|
|
66
|
+
score += (segments.length - i) * 0.1;
|
|
67
|
+
}
|
|
68
|
+
return score;
|
|
69
|
+
}
|
|
70
|
+
/** Normalize trailing slashes */
|
|
71
|
+
normalizeRoute(route) {
|
|
72
|
+
const primary = route.endsWith("/") && route.length > 1 ? route.slice(0, -1) : route;
|
|
73
|
+
const alternate = primary === route ? (route === "/" ? route : route + "/") : route;
|
|
74
|
+
return { primary, alternate };
|
|
75
|
+
}
|
|
76
|
+
/** Find RAI for the given request path and method */
|
|
77
|
+
findRai(path, method) {
|
|
78
|
+
const normalizedMethod = method.trim().toUpperCase();
|
|
79
|
+
const { primary, alternate } = this.normalizeRoute(path);
|
|
80
|
+
// 1. Exact match (fastest)
|
|
81
|
+
const exactKey = `${normalizedMethod}:${primary}`;
|
|
82
|
+
if (this.exactRoutes.has(exactKey)) {
|
|
83
|
+
return this.exactRoutes.get(exactKey);
|
|
84
|
+
}
|
|
85
|
+
// 2. Runtime cache for previously matched dynamic routes
|
|
86
|
+
const cacheKey = `${normalizedMethod}:${primary}`;
|
|
87
|
+
if (this.cache.has(cacheKey)) {
|
|
88
|
+
return this.cache.get(cacheKey).rai;
|
|
89
|
+
}
|
|
90
|
+
// 3. Method-specific route matching
|
|
91
|
+
const methodRoutes = this.routesByMethod.get(normalizedMethod);
|
|
92
|
+
if (!methodRoutes) {
|
|
93
|
+
throw new errors.RequestError(`No RAI found for the request URL: ${path} with method: ${method}`, { code: "RAI_NOT_FOUND", route: path, method });
|
|
94
|
+
}
|
|
95
|
+
// 4. Pattern matching (sorted by specificity)
|
|
96
|
+
for (const route of methodRoutes) {
|
|
97
|
+
const matchResult = route.matcher(primary) || route.matcher(alternate);
|
|
98
|
+
if (matchResult) {
|
|
99
|
+
this.cache.set(cacheKey, route);
|
|
100
|
+
// Evict oldest entry if cache is too large
|
|
101
|
+
if (this.cache.size > 1000) {
|
|
102
|
+
const firstKey = this.cache.keys().next().value;
|
|
103
|
+
this.cache.delete(firstKey);
|
|
104
|
+
}
|
|
105
|
+
return route.rai;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// 5. No match
|
|
109
|
+
throw new errors.RequestError(`No RAI found for the request URL: ${path} with method: ${method}`, {
|
|
110
|
+
code: "RAI_NOT_FOUND",
|
|
111
|
+
route: path,
|
|
112
|
+
method,
|
|
113
|
+
availableRoutes: methodRoutes.map((r) => r.path),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
clearCache() {
|
|
117
|
+
this.cache.clear();
|
|
118
|
+
}
|
|
119
|
+
getCacheStats() {
|
|
120
|
+
return {
|
|
121
|
+
size: this.cache.size,
|
|
122
|
+
methods: Array.from(this.routesByMethod.keys()),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export default RaiMatcher;
|
|
127
|
+
//# sourceMappingURL=RaiMatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RaiMatcher.js","sourceRoot":"","sources":["../../src/routing/RaiMatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Handler, RequestHandler } from "express";
|
|
2
|
+
import type { RouteConfig, ErrorTraceType, RAI, DefaultRole } from "../types.js";
|
|
3
|
+
import ErrorCatcher from "./ErrorCatcher.js";
|
|
4
|
+
import type RoutesParam from "./RoutesParam.js";
|
|
5
|
+
/**
|
|
6
|
+
* Single API endpoint definition.
|
|
7
|
+
* Carries method, path, RAI, roles, middlewares, and metadata.
|
|
8
|
+
*/
|
|
9
|
+
declare class Route<TRole extends string = DefaultRole> extends ErrorCatcher {
|
|
10
|
+
id: string;
|
|
11
|
+
method: "GET" | "POST" | "PUT" | "DELETE";
|
|
12
|
+
path: string;
|
|
13
|
+
params?: Array<RoutesParam>;
|
|
14
|
+
middlewares: Handler[] | RequestHandler[] | any[];
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
rai: RAI;
|
|
18
|
+
roles: TRole[];
|
|
19
|
+
meta?: Record<string, unknown>;
|
|
20
|
+
debugInfo: ErrorTraceType;
|
|
21
|
+
constructor(r: RouteConfig<TRole>);
|
|
22
|
+
private validateRoute;
|
|
23
|
+
private handleConfigError;
|
|
24
|
+
/** Check if user roles are authorized to access this route */
|
|
25
|
+
isAuthorized(userRoles: string[]): boolean;
|
|
26
|
+
}
|
|
27
|
+
export default Route;
|
|
28
|
+
//# sourceMappingURL=Route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Route.d.ts","sourceRoot":"","sources":["../../src/routing/Route.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EAEd,GAAG,EACH,WAAW,EACZ,MAAM,aAAa,CAAC;AACrB,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAE7C,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAEhD;;;GAGG;AACH,cAAM,KAAK,CAAC,KAAK,SAAS,MAAM,GAAG,WAAW,CAAE,SAAQ,YAAY;IAClE,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,CAAM;IACjC,WAAW,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,GAAG,GAAG,EAAE,CAAC;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,GAAG,CAAC;IACT,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxB,SAAS,EAAE,cAAc,CAAC;gBAErB,CAAC,EAAE,WAAW,CAAC,KAAK,CAAC;IAqBjC,OAAO,CAAC,aAAa;IAwBrB,OAAO,CAAC,iBAAiB;IAqDzB,8DAA8D;IAC9D,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO;CAS3C;AAED,eAAe,KAAK,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { randomUUID } from "crypto";
|
|
3
|
+
import ErrorCatcher from "./ErrorCatcher.js";
|
|
4
|
+
import errors from "../errors/index.js";
|
|
5
|
+
/**
|
|
6
|
+
* Single API endpoint definition.
|
|
7
|
+
* Carries method, path, RAI, roles, middlewares, and metadata.
|
|
8
|
+
*/
|
|
9
|
+
class Route extends ErrorCatcher {
|
|
10
|
+
id;
|
|
11
|
+
method;
|
|
12
|
+
path;
|
|
13
|
+
params = [];
|
|
14
|
+
middlewares;
|
|
15
|
+
name;
|
|
16
|
+
description;
|
|
17
|
+
rai;
|
|
18
|
+
roles;
|
|
19
|
+
meta;
|
|
20
|
+
debugInfo;
|
|
21
|
+
constructor(r) {
|
|
22
|
+
super();
|
|
23
|
+
this.id = randomUUID();
|
|
24
|
+
this.method = r.method;
|
|
25
|
+
this.path = r.path;
|
|
26
|
+
this.middlewares = r.middlewares;
|
|
27
|
+
this.name = r.name ? r.name : `[${this.method}] ${this.path}`;
|
|
28
|
+
this.description = r.description ? r.description : "";
|
|
29
|
+
this.rai = r.rai;
|
|
30
|
+
this.roles = r.roles;
|
|
31
|
+
this.meta = r.meta;
|
|
32
|
+
this.debugInfo = Route.getCallerInfo();
|
|
33
|
+
try {
|
|
34
|
+
this.validateRoute();
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
this.handleConfigError(err, this.debugInfo);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
validateRoute() {
|
|
41
|
+
if (!this.method) {
|
|
42
|
+
throw new errors.ConfigError("ROUTE_CONFIG:MISSING_METHOD");
|
|
43
|
+
}
|
|
44
|
+
if (!this.path) {
|
|
45
|
+
throw new errors.ConfigError("ROUTE_CONFIG:MISSING_PATH");
|
|
46
|
+
}
|
|
47
|
+
if (!this.rai) {
|
|
48
|
+
throw new errors.ConfigError("ROUTE_CONFIG:MISSING_RAI");
|
|
49
|
+
}
|
|
50
|
+
if (typeof this.rai !== "string") {
|
|
51
|
+
throw new errors.ConfigError("ROUTE_CONFIG:INVALID_RAI_TYPE");
|
|
52
|
+
}
|
|
53
|
+
if (!this.roles) {
|
|
54
|
+
throw new errors.ConfigError("ROUTE_CONFIG:MISSING_ROLES");
|
|
55
|
+
}
|
|
56
|
+
if (!Array.isArray(this.middlewares)) {
|
|
57
|
+
throw new errors.ConfigError("ROUTE_CONFIG:INVALID_MIDDLEWARES_TYPE");
|
|
58
|
+
}
|
|
59
|
+
if (this.middlewares.length === 0) {
|
|
60
|
+
throw new errors.ConfigError("ROUTE_CONFIG:EMPTY_MIDDLEWARES");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
handleConfigError(err, errInfo) {
|
|
64
|
+
const POSSIBLE_ERRORS = {
|
|
65
|
+
"ROUTE_CONFIG:MISSING_METHOD": {
|
|
66
|
+
title: "Missing required 'method' property",
|
|
67
|
+
details: "The Route instance requires a 'method' to be defined",
|
|
68
|
+
fix: "Add a method to your route configuration:\n method: 'GET' | 'POST' | 'PUT' | 'DELETE'",
|
|
69
|
+
},
|
|
70
|
+
"ROUTE_CONFIG:MISSING_PATH": {
|
|
71
|
+
title: "Missing required 'path' property",
|
|
72
|
+
details: "The Route instance requires a 'path' to be defined",
|
|
73
|
+
fix: "Add a path to your route configuration:\n path: '/users/:id'",
|
|
74
|
+
},
|
|
75
|
+
"ROUTE_CONFIG:MISSING_RAI": {
|
|
76
|
+
title: "Missing required 'rai' property",
|
|
77
|
+
details: "The Route instance requires a unique 'rai' (Route Access Identifier)",
|
|
78
|
+
fix: "Add a rai to your route configuration:\n rai: 'users:read' // Must be unique across all routes",
|
|
79
|
+
},
|
|
80
|
+
"ROUTE_CONFIG:INVALID_RAI_TYPE": {
|
|
81
|
+
title: "Invalid 'rai' property type",
|
|
82
|
+
details: "The 'rai' property must be a string, but received: " + typeof this.rai,
|
|
83
|
+
fix: "Ensure rai is a string:\n rai: 'users:read'",
|
|
84
|
+
},
|
|
85
|
+
"ROUTE_CONFIG:DUPLICATE_RAI": {
|
|
86
|
+
title: "Duplicate 'rai' detected",
|
|
87
|
+
details: "The 'rai' provided is already registered: " + this.rai,
|
|
88
|
+
fix: "Ensure each route has a unique rai:\n rai: 'users:create'",
|
|
89
|
+
},
|
|
90
|
+
"ROUTE_CONFIG:MISSING_ROLES": {
|
|
91
|
+
title: "Missing required 'roles' property",
|
|
92
|
+
details: "The Route instance requires a 'roles' array",
|
|
93
|
+
fix: "Add roles to your route configuration:\n roles: ['admin', 'user']",
|
|
94
|
+
},
|
|
95
|
+
"ROUTE_CONFIG:INVALID_MIDDLEWARES_TYPE": {
|
|
96
|
+
title: "Invalid 'middlewares' property type",
|
|
97
|
+
details: "The 'middlewares' property must be an array, but received: " + typeof this.middlewares,
|
|
98
|
+
fix: "Ensure middlewares is an array:\n middlewares: [authMiddleware, validateMiddleware]",
|
|
99
|
+
},
|
|
100
|
+
"ROUTE_CONFIG:EMPTY_MIDDLEWARES": {
|
|
101
|
+
title: "Empty 'middlewares' array",
|
|
102
|
+
details: "The Route instance requires at least one middleware function",
|
|
103
|
+
fix: "Add at least one handler:\n middlewares: [(req, res) => { res.json({ data: 'example' }) }]",
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
const errorConfig = POSSIBLE_ERRORS[err.message] || {
|
|
107
|
+
title: err.message,
|
|
108
|
+
details: "An unexpected error occurred",
|
|
109
|
+
fix: "Please check your Route configuration",
|
|
110
|
+
};
|
|
111
|
+
ErrorCatcher.logError(errorConfig, errInfo);
|
|
112
|
+
}
|
|
113
|
+
/** Check if user roles are authorized to access this route */
|
|
114
|
+
isAuthorized(userRoles) {
|
|
115
|
+
if (this.roles.length === 0)
|
|
116
|
+
return false;
|
|
117
|
+
for (const role of userRoles) {
|
|
118
|
+
if (this.roles.includes(role))
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export default Route;
|
|
125
|
+
//# sourceMappingURL=Route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Route.js","sourceRoot":"","sources":["../../src/routing/Route.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Handler } from "express";
|
|
2
|
+
import Route from "./Route.js";
|
|
3
|
+
import type { RoutesConfig, DefaultRole } from "../types.js";
|
|
4
|
+
import RoutesParam from "./RoutesParam.js";
|
|
5
|
+
import ErrorCatcher from "./ErrorCatcher.js";
|
|
6
|
+
/**
|
|
7
|
+
* Hierarchical route group with shared prefix, middleware, and params.
|
|
8
|
+
* Contains Route (endpoints) and/or nested Routes (sub-groups).
|
|
9
|
+
*/
|
|
10
|
+
declare class Routes<TRole extends string = DefaultRole> extends ErrorCatcher {
|
|
11
|
+
prefix: string;
|
|
12
|
+
routes: Array<Route<TRole> | Routes<TRole>>;
|
|
13
|
+
params?: RoutesParam[];
|
|
14
|
+
middlewares?: Handler[];
|
|
15
|
+
mergeParams?: boolean;
|
|
16
|
+
meta?: Record<string, unknown>;
|
|
17
|
+
constructor(r: RoutesConfig<TRole>);
|
|
18
|
+
private validateRoutes;
|
|
19
|
+
private handleConfigError;
|
|
20
|
+
/**
|
|
21
|
+
* Flatten the route tree into a Map<RAI, enriched Route>.
|
|
22
|
+
* Accumulates paths, middlewares, and params from parent groups.
|
|
23
|
+
*/
|
|
24
|
+
groupRoutesByRai(): Map<string, Route<TRole> & {
|
|
25
|
+
params: RoutesParam[];
|
|
26
|
+
middlewares: Handler[];
|
|
27
|
+
}>;
|
|
28
|
+
private deduplicateParams;
|
|
29
|
+
}
|
|
30
|
+
export default Routes;
|
|
31
|
+
//# sourceMappingURL=Routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Routes.d.ts","sourceRoot":"","sources":["../../src/routing/Routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,KAAK,EAGV,YAAY,EACZ,WAAW,EACZ,MAAM,aAAa,CAAC;AACrB,OAAO,WAAW,MAAM,kBAAkB,CAAC;AAC3C,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAG7C;;;GAGG;AACH,cAAM,MAAM,CAAC,KAAK,SAAS,MAAM,GAAG,WAAW,CAAE,SAAQ,YAAY;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5C,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAS;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAEnB,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC;IAkBlC,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,iBAAiB;IA0CzB;;;OAGG;IACI,gBAAgB,IAAI,GAAG,CAC5B,MAAM,EACN,KAAK,CAAC,KAAK,CAAC,GAAG;QAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QAAC,WAAW,EAAE,OAAO,EAAE,CAAA;KAAE,CACjE;IA6ED,OAAO,CAAC,iBAAiB;CAK1B;AAED,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import Route from "./Route.js";
|
|
2
|
+
import ErrorCatcher from "./ErrorCatcher.js";
|
|
3
|
+
import errors from "../errors/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Hierarchical route group with shared prefix, middleware, and params.
|
|
6
|
+
* Contains Route (endpoints) and/or nested Routes (sub-groups).
|
|
7
|
+
*/
|
|
8
|
+
class Routes extends ErrorCatcher {
|
|
9
|
+
prefix;
|
|
10
|
+
routes;
|
|
11
|
+
params;
|
|
12
|
+
middlewares;
|
|
13
|
+
mergeParams = false;
|
|
14
|
+
meta;
|
|
15
|
+
constructor(r) {
|
|
16
|
+
super();
|
|
17
|
+
this.prefix = r.prefix ? r.prefix : "";
|
|
18
|
+
this.routes = r.routes;
|
|
19
|
+
this.params = r.params ? r.params : [];
|
|
20
|
+
this.middlewares = r.middlewares ? r.middlewares : [];
|
|
21
|
+
this.mergeParams = r.mergeParams ? r.mergeParams : false;
|
|
22
|
+
this.meta = r.meta;
|
|
23
|
+
try {
|
|
24
|
+
this.validateRoutes();
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
const errInfo = Routes.getCallerInfo();
|
|
28
|
+
this.handleConfigError(err, errInfo);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
validateRoutes() {
|
|
32
|
+
if (this.prefix && typeof this.prefix !== "string") {
|
|
33
|
+
throw new errors.ConfigError("ROUTES_CONFIG:INVALID_PREFIX_TYPE");
|
|
34
|
+
}
|
|
35
|
+
if (!this.routes) {
|
|
36
|
+
throw new errors.ConfigError("ROUTES_CONFIG:MISSING_ROUTES");
|
|
37
|
+
}
|
|
38
|
+
if (!Array.isArray(this.routes)) {
|
|
39
|
+
throw new Error("ROUTES_CONFIG:INVALID_ROUTES_TYPE");
|
|
40
|
+
}
|
|
41
|
+
if (this.middlewares && !Array.isArray(this.middlewares)) {
|
|
42
|
+
throw new errors.ConfigError("ROUTES_CONFIG:INVALID_MIDDLEWARES_TYPE");
|
|
43
|
+
}
|
|
44
|
+
if (this.params && !Array.isArray(this.params)) {
|
|
45
|
+
throw new errors.ConfigError("ROUTES_CONFIG:INVALID_PARAMS_TYPE");
|
|
46
|
+
}
|
|
47
|
+
if (this.mergeParams && typeof this.mergeParams !== "boolean") {
|
|
48
|
+
throw new errors.ConfigError("ROUTES_CONFIG:INVALID_MERGE_PARAMS_TYPE");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
handleConfigError(err, errInfo) {
|
|
52
|
+
const POSSIBLE_ERRORS = {
|
|
53
|
+
"ROUTES_CONFIG:INVALID_PREFIX_TYPE": {
|
|
54
|
+
title: "Invalid 'prefix' property type",
|
|
55
|
+
details: "The 'prefix' must be a string, but received: " + typeof this.prefix,
|
|
56
|
+
fix: "Ensure prefix is a string:\n prefix: '/api' or prefix: ''",
|
|
57
|
+
},
|
|
58
|
+
"ROUTES_CONFIG:MISSING_ROUTES": {
|
|
59
|
+
title: "Missing required 'routes' property",
|
|
60
|
+
details: "The Routes instance requires a 'routes' array",
|
|
61
|
+
fix: "Add a routes array:\n routes: [new Routes(...), new Route(...)]",
|
|
62
|
+
},
|
|
63
|
+
"ROUTES_CONFIG:INVALID_ROUTES_TYPE": {
|
|
64
|
+
title: "Invalid 'routes' property type",
|
|
65
|
+
details: "The 'routes' must be an array, but received: " + typeof this.routes,
|
|
66
|
+
fix: "Ensure routes is an array:\n routes: [...]",
|
|
67
|
+
},
|
|
68
|
+
"ROUTES_CONFIG:INVALID_MIDDLEWARES_TYPE": {
|
|
69
|
+
title: "Invalid 'middlewares' property type",
|
|
70
|
+
details: "The 'middlewares' must be an array of express handlers, but received: " + typeof this.middlewares,
|
|
71
|
+
fix: "Provide an array of middleware:\n middlewares: [middleware1, middleware2]",
|
|
72
|
+
},
|
|
73
|
+
"ROUTES_CONFIG:INVALID_PARAMS_TYPE": {
|
|
74
|
+
title: "Invalid 'params' property type",
|
|
75
|
+
details: "The 'params' must be an array of RoutesParam instances, but received: " + typeof this.params,
|
|
76
|
+
fix: "Provide an array of RoutesParam:\n params: [new RoutesParam(...)]",
|
|
77
|
+
},
|
|
78
|
+
"ROUTES_CONFIG:INVALID_MERGE_PARAMS_TYPE": {
|
|
79
|
+
title: "Invalid 'mergeParams' property type",
|
|
80
|
+
details: "The 'mergeParams' must be a boolean, but received: " + typeof this.mergeParams,
|
|
81
|
+
fix: "Set mergeParams to a boolean:\n mergeParams: true",
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
const errorConfig = POSSIBLE_ERRORS[err.message] || {
|
|
85
|
+
title: err.message,
|
|
86
|
+
details: "An unexpected error occurred",
|
|
87
|
+
fix: "Please check your Routes configuration",
|
|
88
|
+
};
|
|
89
|
+
ErrorCatcher.logError(errorConfig, errInfo);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Flatten the route tree into a Map<RAI, enriched Route>.
|
|
93
|
+
* Accumulates paths, middlewares, and params from parent groups.
|
|
94
|
+
*/
|
|
95
|
+
groupRoutesByRai() {
|
|
96
|
+
const raiMap = new Map();
|
|
97
|
+
const traverse = (current, accumulatedPath, accumulatedParams, accumulatedMiddlewares, parentsMergeParams) => {
|
|
98
|
+
// Handle endpoint
|
|
99
|
+
if (current instanceof Route) {
|
|
100
|
+
const fullPath = accumulatedPath + current.path;
|
|
101
|
+
const finalMiddlewares = [
|
|
102
|
+
...accumulatedMiddlewares,
|
|
103
|
+
...(current.middlewares || []),
|
|
104
|
+
];
|
|
105
|
+
const finalParams = this.deduplicateParams(accumulatedParams);
|
|
106
|
+
if (raiMap.has(current.rai)) {
|
|
107
|
+
const errorConfig = {
|
|
108
|
+
title: "Duplicate 'rai' detected",
|
|
109
|
+
details: "The 'rai' provided is already registered: " + current.rai,
|
|
110
|
+
fix: "Ensure each route has a unique rai:\n rai: 'users:create'",
|
|
111
|
+
};
|
|
112
|
+
ErrorCatcher.logError(errorConfig, current.debugInfo);
|
|
113
|
+
}
|
|
114
|
+
const extendedRoute = Object.assign(Object.create(Object.getPrototypeOf(current)), current, {
|
|
115
|
+
path: fullPath,
|
|
116
|
+
params: finalParams,
|
|
117
|
+
middlewares: finalMiddlewares,
|
|
118
|
+
});
|
|
119
|
+
raiMap.set(current.rai, extendedRoute);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
// Handle group
|
|
123
|
+
if (current instanceof Routes) {
|
|
124
|
+
const nextPath = accumulatedPath + current.prefix;
|
|
125
|
+
const nextMiddlewares = [
|
|
126
|
+
...accumulatedMiddlewares,
|
|
127
|
+
...(current.middlewares || []),
|
|
128
|
+
];
|
|
129
|
+
let nextParams = [];
|
|
130
|
+
if (parentsMergeParams) {
|
|
131
|
+
nextParams = [...accumulatedParams, ...(current.params || [])];
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
nextParams = current.params || [];
|
|
135
|
+
}
|
|
136
|
+
current.routes.forEach((child) => {
|
|
137
|
+
traverse(child, nextPath, nextParams, nextMiddlewares, current.mergeParams || false);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
traverse(this, "", [], [], false);
|
|
142
|
+
return raiMap;
|
|
143
|
+
}
|
|
144
|
+
deduplicateParams(params) {
|
|
145
|
+
const unique = new Map();
|
|
146
|
+
params.forEach((p) => unique.set(p.path, p));
|
|
147
|
+
return Array.from(unique.values());
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export default Routes;
|
|
151
|
+
//# sourceMappingURL=Routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Routes.js","sourceRoot":"","sources":["../../src/routing/Routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { RequestParamHandler } from "express";
|
|
2
|
+
import ErrorCatcher from "./ErrorCatcher.js";
|
|
3
|
+
/**
|
|
4
|
+
* Parameter handler with optional validation rules.
|
|
5
|
+
* Attached to Routes to validate and process URL params (e.g. :userId).
|
|
6
|
+
*/
|
|
7
|
+
declare class RoutesParam extends ErrorCatcher {
|
|
8
|
+
readonly uuid: string;
|
|
9
|
+
path: string;
|
|
10
|
+
middleware: RequestParamHandler;
|
|
11
|
+
validate?: {
|
|
12
|
+
pattern?: RegExp;
|
|
13
|
+
minLength?: number;
|
|
14
|
+
maxLength?: number;
|
|
15
|
+
validatorFn?: (value: string) => boolean;
|
|
16
|
+
};
|
|
17
|
+
constructor(path: string, middleware: RequestParamHandler, validate?: {
|
|
18
|
+
pattern?: RegExp;
|
|
19
|
+
minLength?: number;
|
|
20
|
+
maxLength?: number;
|
|
21
|
+
validatorFn?: (value: string) => boolean;
|
|
22
|
+
});
|
|
23
|
+
private validateParam;
|
|
24
|
+
private handleConfigError;
|
|
25
|
+
/** Run all validation rules against a param value */
|
|
26
|
+
validateValue(value: string): boolean;
|
|
27
|
+
}
|
|
28
|
+
export default RoutesParam;
|
|
29
|
+
//# sourceMappingURL=RoutesParam.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RoutesParam.d.ts","sourceRoot":"","sources":["../../src/routing/RoutesParam.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAI7C;;;GAGG;AACH,cAAM,WAAY,SAAQ,YAAY;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;KAC1C,CAAC;gBAGA,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,mBAAmB,EAC/B,QAAQ,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;KAC1C;IAiBH,OAAO,CAAC,aAAa;IA4BrB,OAAO,CAAC,iBAAiB;IAgDzB,qDAAqD;IACrD,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CAUtC;AAED,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
import ErrorCatcher from "./ErrorCatcher.js";
|
|
3
|
+
import errors from "../errors/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Parameter handler with optional validation rules.
|
|
6
|
+
* Attached to Routes to validate and process URL params (e.g. :userId).
|
|
7
|
+
*/
|
|
8
|
+
class RoutesParam extends ErrorCatcher {
|
|
9
|
+
uuid;
|
|
10
|
+
path;
|
|
11
|
+
middleware;
|
|
12
|
+
validate;
|
|
13
|
+
constructor(path, middleware, validate) {
|
|
14
|
+
super();
|
|
15
|
+
this.uuid = randomUUID();
|
|
16
|
+
this.path = path;
|
|
17
|
+
this.middleware = middleware;
|
|
18
|
+
this.validate = validate;
|
|
19
|
+
try {
|
|
20
|
+
this.validateParam();
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
const errInfo = ErrorCatcher.getCallerInfo();
|
|
24
|
+
this.handleConfigError(err, errInfo);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
validateParam() {
|
|
28
|
+
if (!this.path) {
|
|
29
|
+
throw new errors.ConfigError("ROUTES_PARAM:MISSING_PATH");
|
|
30
|
+
}
|
|
31
|
+
if (!this.middleware) {
|
|
32
|
+
throw new errors.ConfigError("ROUTES_PARAM:MISSING_MIDDLEWARE");
|
|
33
|
+
}
|
|
34
|
+
if (this.validate) {
|
|
35
|
+
if (typeof this.validate !== "object" || Array.isArray(this.validate)) {
|
|
36
|
+
throw new errors.ConfigError("ROUTES_PARAM:INVALID_VALIDATE_TYPE");
|
|
37
|
+
}
|
|
38
|
+
if (this.validate.pattern && !(this.validate.pattern instanceof RegExp)) {
|
|
39
|
+
throw new errors.ConfigError("ROUTES_PARAM:INVALID_PATTERN_TYPE");
|
|
40
|
+
}
|
|
41
|
+
if (this.validate.minLength !== undefined && typeof this.validate.minLength !== "number") {
|
|
42
|
+
throw new errors.ConfigError("ROUTES_PARAM:INVALID_MIN_LENGTH_TYPE");
|
|
43
|
+
}
|
|
44
|
+
if (this.validate.maxLength !== undefined && typeof this.validate.maxLength !== "number") {
|
|
45
|
+
throw new errors.ConfigError("ROUTES_PARAM:INVALID_MAX_LENGTH_TYPE");
|
|
46
|
+
}
|
|
47
|
+
if (this.validate.validatorFn && typeof this.validate.validatorFn !== "function") {
|
|
48
|
+
throw new errors.ConfigError("ROUTES_PARAM:INVALID_VALIDATOR_FN_TYPE");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
handleConfigError(err, errInfo) {
|
|
53
|
+
const POSSIBLE_ERRORS = {
|
|
54
|
+
"ROUTES_PARAM:INVALID_VALIDATE_TYPE": {
|
|
55
|
+
title: "Invalid 'validate' property type",
|
|
56
|
+
details: "The 'validate' property must be an object, but received: " + typeof this.validate,
|
|
57
|
+
fix: "Ensure validate is an object:\n validate: { pattern: /regex/, minLength: 3 }",
|
|
58
|
+
},
|
|
59
|
+
"ROUTES_PARAM:MISSING_PATH": {
|
|
60
|
+
title: "Missing required 'path' parameter",
|
|
61
|
+
details: "The RoutesParam instance requires a 'path' to be defined",
|
|
62
|
+
fix: "Provide a path parameter:\n new RoutesParam('userId', middleware)",
|
|
63
|
+
},
|
|
64
|
+
"ROUTES_PARAM:MISSING_MIDDLEWARE": {
|
|
65
|
+
title: "Missing required 'middleware' parameter",
|
|
66
|
+
details: "The RoutesParam instance requires a 'middleware' function",
|
|
67
|
+
fix: "Provide a middleware function:\n new RoutesParam('userId', (req, res, next, id) => { next() })",
|
|
68
|
+
},
|
|
69
|
+
"ROUTES_PARAM:INVALID_PATTERN_TYPE": {
|
|
70
|
+
title: "Invalid 'validate.pattern' property type",
|
|
71
|
+
details: "The 'validate.pattern' must be a RegExp, but received: " + typeof this.validate?.pattern,
|
|
72
|
+
fix: "Ensure pattern is a RegExp:\n validate: { pattern: /^[0-9]+$/ }",
|
|
73
|
+
},
|
|
74
|
+
"ROUTES_PARAM:INVALID_MIN_LENGTH_TYPE": {
|
|
75
|
+
title: "Invalid 'validate.minLength' property type",
|
|
76
|
+
details: "The 'validate.minLength' must be a number, but received: " + typeof this.validate?.minLength,
|
|
77
|
+
fix: "Ensure minLength is a number:\n validate: { minLength: 3 }",
|
|
78
|
+
},
|
|
79
|
+
"ROUTES_PARAM:INVALID_MAX_LENGTH_TYPE": {
|
|
80
|
+
title: "Invalid 'validate.maxLength' property type",
|
|
81
|
+
details: "The 'validate.maxLength' must be a number, but received: " + typeof this.validate?.maxLength,
|
|
82
|
+
fix: "Ensure maxLength is a number:\n validate: { maxLength: 50 }",
|
|
83
|
+
},
|
|
84
|
+
"ROUTES_PARAM:INVALID_VALIDATOR_FN_TYPE": {
|
|
85
|
+
title: "Invalid 'validate.validatorFn' property type",
|
|
86
|
+
details: "The 'validate.validatorFn' must be a function, but received: " + typeof this.validate?.validatorFn,
|
|
87
|
+
fix: "Ensure validatorFn is a function:\n validate: { validatorFn: (value) => value.startsWith('user-') }",
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
const errorConfig = POSSIBLE_ERRORS[err.message] || {
|
|
91
|
+
title: err.message,
|
|
92
|
+
details: "An unexpected error occurred",
|
|
93
|
+
fix: "Please check your RoutesParam configuration",
|
|
94
|
+
};
|
|
95
|
+
ErrorCatcher.logError(errorConfig, errInfo);
|
|
96
|
+
}
|
|
97
|
+
/** Run all validation rules against a param value */
|
|
98
|
+
validateValue(value) {
|
|
99
|
+
if (!this.validate)
|
|
100
|
+
return true;
|
|
101
|
+
if (this.validate.pattern && !this.validate.pattern.test(value))
|
|
102
|
+
return false;
|
|
103
|
+
if (this.validate.minLength !== undefined && value.length < this.validate.minLength)
|
|
104
|
+
return false;
|
|
105
|
+
if (this.validate.maxLength !== undefined && value.length > this.validate.maxLength)
|
|
106
|
+
return false;
|
|
107
|
+
if (this.validate.validatorFn && !this.validate.validatorFn(value))
|
|
108
|
+
return false;
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export default RoutesParam;
|
|
113
|
+
//# sourceMappingURL=RoutesParam.js.map
|