azurajs 1.0.2 → 1.0.4
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/LICENSE +673 -673
- package/README.md +501 -501
- package/package.json +1 -1
- package/src/decorators/Route.ts +141 -141
- package/src/decorators/index.ts +24 -24
- package/src/index.ts +27 -27
- package/src/infra/Router.ts +56 -56
- package/src/infra/Server.ts +297 -274
- package/src/infra/utils/GetIp.ts +15 -15
- package/src/infra/utils/GetOpenPort.ts +15 -15
- package/src/infra/utils/HttpError.ts +9 -9
- package/src/infra/utils/RequestHandler.ts +33 -33
- package/src/infra/utils/route/Node.ts +15 -15
- package/src/middleware/LoggingMiddleware.ts +108 -108
- package/src/middleware/index.ts +14 -14
- package/src/shared/config/ConfigModule.ts +115 -113
- package/src/shared/plugins/CORSPlugin.ts +28 -0
- package/src/shared/plugins/RateLimitPlugin.ts +32 -0
- package/src/types/common.type.ts +9 -4
- package/src/types/http/request.type.ts +20 -20
- package/src/types/http/response.type.ts +32 -32
- package/src/types/plugins/cors.type.ts +5 -0
- package/src/types/routes.type.ts +23 -23
- package/src/types/validations.type.ts +1 -1
- package/src/utils/Logger.ts +23 -5
- package/src/utils/Parser.ts +19 -19
- package/src/utils/cookies/ParserCookie.ts +9 -9
- package/src/utils/cookies/SerializeCookie.ts +15 -15
- package/src/utils/validators/DTOValidator.ts +30 -30
- package/src/utils/validators/SchemaValidator.ts +37 -37
package/package.json
CHANGED
package/src/decorators/Route.ts
CHANGED
|
@@ -1,141 +1,141 @@
|
|
|
1
|
-
import type { AzuraClient } from "../infra/Server";
|
|
2
|
-
import type { RequestServer } from "../types/http/request.type";
|
|
3
|
-
import type { ResponseServer } from "../types/http/response.type";
|
|
4
|
-
import type { ParamDefinition, ParamSource, RouteDefinition } from "../types/routes.type";
|
|
5
|
-
|
|
6
|
-
const PREFIX = new WeakMap<Function, string>();
|
|
7
|
-
const ROUTES = new WeakMap<Function, RouteDefinition[]>();
|
|
8
|
-
const PARAMS = new WeakMap<Function, Map<string, ParamDefinition[]>>();
|
|
9
|
-
|
|
10
|
-
export function Controller(prefix = ""): ClassDecorator {
|
|
11
|
-
return (target) => {
|
|
12
|
-
PREFIX.set(target as Function, prefix);
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function createMethodDecorator(method: string) {
|
|
17
|
-
return function (path = ""): MethodDecorator {
|
|
18
|
-
return (target, propertyKey) => {
|
|
19
|
-
const ctor =
|
|
20
|
-
typeof target === "function" ? (target as Function) : (target as any).constructor;
|
|
21
|
-
const key = String(propertyKey);
|
|
22
|
-
const routes = ROUTES.get(ctor) ?? [];
|
|
23
|
-
const params = PARAMS.get(ctor)?.get(key) ?? [];
|
|
24
|
-
|
|
25
|
-
const exists = routes.some(
|
|
26
|
-
(r) => r.method === method && r.path === path && r.propertyKey === key
|
|
27
|
-
);
|
|
28
|
-
if (!exists) {
|
|
29
|
-
routes.push({
|
|
30
|
-
method,
|
|
31
|
-
path,
|
|
32
|
-
propertyKey: key,
|
|
33
|
-
params,
|
|
34
|
-
});
|
|
35
|
-
ROUTES.set(ctor, routes);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function createParamDecorator(type: ParamSource) {
|
|
42
|
-
return function (name?: string): ParameterDecorator {
|
|
43
|
-
return (target, propertyKey, parameterIndex) => {
|
|
44
|
-
const ctor =
|
|
45
|
-
typeof target === "function" ? (target as Function) : (target as any).constructor;
|
|
46
|
-
let map = PARAMS.get(ctor);
|
|
47
|
-
if (!map) {
|
|
48
|
-
map = new Map<string, ParamDefinition[]>();
|
|
49
|
-
PARAMS.set(ctor, map);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const key = String(propertyKey);
|
|
53
|
-
const list = map.get(key) ?? [];
|
|
54
|
-
const exists = list.some(
|
|
55
|
-
(p) => p.index === parameterIndex && p.type === type && p.name === name
|
|
56
|
-
);
|
|
57
|
-
if (!exists) {
|
|
58
|
-
list.push({
|
|
59
|
-
index: parameterIndex,
|
|
60
|
-
type,
|
|
61
|
-
name,
|
|
62
|
-
});
|
|
63
|
-
map.set(key, list);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export const Get = createMethodDecorator("GET");
|
|
70
|
-
export const Post = createMethodDecorator("POST");
|
|
71
|
-
export const Put = createMethodDecorator("PUT");
|
|
72
|
-
export const Delete = createMethodDecorator("DELETE");
|
|
73
|
-
export const Patch = createMethodDecorator("PATCH");
|
|
74
|
-
export const Head = createMethodDecorator("HEAD");
|
|
75
|
-
export const Options = createMethodDecorator("OPTIONS");
|
|
76
|
-
|
|
77
|
-
export const Req = createParamDecorator("req");
|
|
78
|
-
export const Res = createParamDecorator("res");
|
|
79
|
-
export const Next = createParamDecorator("next");
|
|
80
|
-
export const Param = createParamDecorator("param");
|
|
81
|
-
export const Query = createParamDecorator("query");
|
|
82
|
-
export const Body = createParamDecorator("body");
|
|
83
|
-
export const Headers = createParamDecorator("headers");
|
|
84
|
-
export const Ip = createParamDecorator("ip");
|
|
85
|
-
export const UserAgent = createParamDecorator("useragent");
|
|
86
|
-
|
|
87
|
-
export function applyDecorators(app: AzuraClient, controllers: Array<new () => any>) {
|
|
88
|
-
controllers.forEach((ControllerClass) => {
|
|
89
|
-
const prefix = PREFIX.get(ControllerClass) ?? "";
|
|
90
|
-
const instance = new ControllerClass();
|
|
91
|
-
const routes = ROUTES.get(ControllerClass) ?? [];
|
|
92
|
-
|
|
93
|
-
routes.forEach((r) => {
|
|
94
|
-
const handler = async (
|
|
95
|
-
req: RequestServer,
|
|
96
|
-
res: ResponseServer,
|
|
97
|
-
next: (err?: unknown) => void
|
|
98
|
-
) => {
|
|
99
|
-
try {
|
|
100
|
-
const params = (r.params ?? []).slice().sort((a, b) => a.index - b.index);
|
|
101
|
-
const args = params.map((p) => {
|
|
102
|
-
switch (p.type) {
|
|
103
|
-
case "req":
|
|
104
|
-
return req;
|
|
105
|
-
case "res":
|
|
106
|
-
return res;
|
|
107
|
-
case "next":
|
|
108
|
-
return next;
|
|
109
|
-
case "param":
|
|
110
|
-
return p.name ? req.params[p.name] : req.params;
|
|
111
|
-
case "query":
|
|
112
|
-
return p.name ? req.query[p.name] : req.query;
|
|
113
|
-
case "body": {
|
|
114
|
-
const body = req.body as Record<string, unknown> | undefined;
|
|
115
|
-
return p.name ? body?.[p.name] : body;
|
|
116
|
-
}
|
|
117
|
-
case "headers":
|
|
118
|
-
return p.name ? req.headers[p.name.toLowerCase()] : req.headers;
|
|
119
|
-
case "ip":
|
|
120
|
-
return req.ip;
|
|
121
|
-
case "useragent":
|
|
122
|
-
return req.headers["user-agent"];
|
|
123
|
-
default:
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
const fn = (instance as Record<string, unknown>)[r.propertyKey] as (
|
|
129
|
-
...args: unknown[]
|
|
130
|
-
) => unknown;
|
|
131
|
-
const result = fn(...args);
|
|
132
|
-
if (result instanceof Promise) await result;
|
|
133
|
-
} catch (err) {
|
|
134
|
-
next(err);
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
app.addRoute(r.method, prefix + r.path, handler);
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
}
|
|
1
|
+
import type { AzuraClient } from "../infra/Server";
|
|
2
|
+
import type { RequestServer } from "../types/http/request.type";
|
|
3
|
+
import type { ResponseServer } from "../types/http/response.type";
|
|
4
|
+
import type { ParamDefinition, ParamSource, RouteDefinition } from "../types/routes.type";
|
|
5
|
+
|
|
6
|
+
const PREFIX = new WeakMap<Function, string>();
|
|
7
|
+
const ROUTES = new WeakMap<Function, RouteDefinition[]>();
|
|
8
|
+
const PARAMS = new WeakMap<Function, Map<string, ParamDefinition[]>>();
|
|
9
|
+
|
|
10
|
+
export function Controller(prefix = ""): ClassDecorator {
|
|
11
|
+
return (target) => {
|
|
12
|
+
PREFIX.set(target as Function, prefix);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function createMethodDecorator(method: string) {
|
|
17
|
+
return function (path = ""): MethodDecorator {
|
|
18
|
+
return (target, propertyKey) => {
|
|
19
|
+
const ctor =
|
|
20
|
+
typeof target === "function" ? (target as Function) : (target as any).constructor;
|
|
21
|
+
const key = String(propertyKey);
|
|
22
|
+
const routes = ROUTES.get(ctor) ?? [];
|
|
23
|
+
const params = PARAMS.get(ctor)?.get(key) ?? [];
|
|
24
|
+
|
|
25
|
+
const exists = routes.some(
|
|
26
|
+
(r) => r.method === method && r.path === path && r.propertyKey === key
|
|
27
|
+
);
|
|
28
|
+
if (!exists) {
|
|
29
|
+
routes.push({
|
|
30
|
+
method,
|
|
31
|
+
path,
|
|
32
|
+
propertyKey: key,
|
|
33
|
+
params,
|
|
34
|
+
});
|
|
35
|
+
ROUTES.set(ctor, routes);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function createParamDecorator(type: ParamSource) {
|
|
42
|
+
return function (name?: string): ParameterDecorator {
|
|
43
|
+
return (target, propertyKey, parameterIndex) => {
|
|
44
|
+
const ctor =
|
|
45
|
+
typeof target === "function" ? (target as Function) : (target as any).constructor;
|
|
46
|
+
let map = PARAMS.get(ctor);
|
|
47
|
+
if (!map) {
|
|
48
|
+
map = new Map<string, ParamDefinition[]>();
|
|
49
|
+
PARAMS.set(ctor, map);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const key = String(propertyKey);
|
|
53
|
+
const list = map.get(key) ?? [];
|
|
54
|
+
const exists = list.some(
|
|
55
|
+
(p) => p.index === parameterIndex && p.type === type && p.name === name
|
|
56
|
+
);
|
|
57
|
+
if (!exists) {
|
|
58
|
+
list.push({
|
|
59
|
+
index: parameterIndex,
|
|
60
|
+
type,
|
|
61
|
+
name,
|
|
62
|
+
});
|
|
63
|
+
map.set(key, list);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const Get = createMethodDecorator("GET");
|
|
70
|
+
export const Post = createMethodDecorator("POST");
|
|
71
|
+
export const Put = createMethodDecorator("PUT");
|
|
72
|
+
export const Delete = createMethodDecorator("DELETE");
|
|
73
|
+
export const Patch = createMethodDecorator("PATCH");
|
|
74
|
+
export const Head = createMethodDecorator("HEAD");
|
|
75
|
+
export const Options = createMethodDecorator("OPTIONS");
|
|
76
|
+
|
|
77
|
+
export const Req = createParamDecorator("req");
|
|
78
|
+
export const Res = createParamDecorator("res");
|
|
79
|
+
export const Next = createParamDecorator("next");
|
|
80
|
+
export const Param = createParamDecorator("param");
|
|
81
|
+
export const Query = createParamDecorator("query");
|
|
82
|
+
export const Body = createParamDecorator("body");
|
|
83
|
+
export const Headers = createParamDecorator("headers");
|
|
84
|
+
export const Ip = createParamDecorator("ip");
|
|
85
|
+
export const UserAgent = createParamDecorator("useragent");
|
|
86
|
+
|
|
87
|
+
export function applyDecorators(app: AzuraClient, controllers: Array<new () => any>) {
|
|
88
|
+
controllers.forEach((ControllerClass) => {
|
|
89
|
+
const prefix = PREFIX.get(ControllerClass) ?? "";
|
|
90
|
+
const instance = new ControllerClass();
|
|
91
|
+
const routes = ROUTES.get(ControllerClass) ?? [];
|
|
92
|
+
|
|
93
|
+
routes.forEach((r) => {
|
|
94
|
+
const handler = async (
|
|
95
|
+
req: RequestServer,
|
|
96
|
+
res: ResponseServer,
|
|
97
|
+
next: (err?: unknown) => void
|
|
98
|
+
) => {
|
|
99
|
+
try {
|
|
100
|
+
const params = (r.params ?? []).slice().sort((a, b) => a.index - b.index);
|
|
101
|
+
const args = params.map((p) => {
|
|
102
|
+
switch (p.type) {
|
|
103
|
+
case "req":
|
|
104
|
+
return req;
|
|
105
|
+
case "res":
|
|
106
|
+
return res;
|
|
107
|
+
case "next":
|
|
108
|
+
return next;
|
|
109
|
+
case "param":
|
|
110
|
+
return p.name ? req.params[p.name] : req.params;
|
|
111
|
+
case "query":
|
|
112
|
+
return p.name ? req.query[p.name] : req.query;
|
|
113
|
+
case "body": {
|
|
114
|
+
const body = req.body as Record<string, unknown> | undefined;
|
|
115
|
+
return p.name ? body?.[p.name] : body;
|
|
116
|
+
}
|
|
117
|
+
case "headers":
|
|
118
|
+
return p.name ? req.headers[p.name.toLowerCase()] : req.headers;
|
|
119
|
+
case "ip":
|
|
120
|
+
return req.ip;
|
|
121
|
+
case "useragent":
|
|
122
|
+
return req.headers["user-agent"];
|
|
123
|
+
default:
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const fn = (instance as Record<string, unknown>)[r.propertyKey] as (
|
|
129
|
+
...args: unknown[]
|
|
130
|
+
) => unknown;
|
|
131
|
+
const result = fn(...args);
|
|
132
|
+
if (result instanceof Promise) await result;
|
|
133
|
+
} catch (err) {
|
|
134
|
+
next(err);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
app.addRoute(r.method, prefix + r.path, handler);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
}
|
package/src/decorators/index.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
export {
|
|
2
|
-
Controller,
|
|
3
|
-
Get,
|
|
4
|
-
Post,
|
|
5
|
-
Put,
|
|
6
|
-
Delete,
|
|
7
|
-
Patch,
|
|
8
|
-
Head,
|
|
9
|
-
Options,
|
|
10
|
-
Req,
|
|
11
|
-
Res,
|
|
12
|
-
Next,
|
|
13
|
-
Param,
|
|
14
|
-
Query,
|
|
15
|
-
Body,
|
|
16
|
-
Headers,
|
|
17
|
-
Ip,
|
|
18
|
-
UserAgent,
|
|
19
|
-
applyDecorators,
|
|
20
|
-
} from "./Route";
|
|
21
|
-
|
|
22
|
-
export type { RouteDefinition, ParamDefinition, ParamSource } from "../types/routes.type";
|
|
23
|
-
export type { RequestServer } from "../types/http/request.type";
|
|
24
|
-
export type { ResponseServer } from "../types/http/response.type";
|
|
1
|
+
export {
|
|
2
|
+
Controller,
|
|
3
|
+
Get,
|
|
4
|
+
Post,
|
|
5
|
+
Put,
|
|
6
|
+
Delete,
|
|
7
|
+
Patch,
|
|
8
|
+
Head,
|
|
9
|
+
Options,
|
|
10
|
+
Req,
|
|
11
|
+
Res,
|
|
12
|
+
Next,
|
|
13
|
+
Param,
|
|
14
|
+
Query,
|
|
15
|
+
Body,
|
|
16
|
+
Headers,
|
|
17
|
+
Ip,
|
|
18
|
+
UserAgent,
|
|
19
|
+
applyDecorators,
|
|
20
|
+
} from "./Route";
|
|
21
|
+
|
|
22
|
+
export type { RouteDefinition, ParamDefinition, ParamSource } from "../types/routes.type";
|
|
23
|
+
export type { RequestServer } from "../types/http/request.type";
|
|
24
|
+
export type { ResponseServer } from "../types/http/response.type";
|
package/src/index.ts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
// Core Server & Router
|
|
2
|
-
export * from "./infra/Server";
|
|
3
|
-
export { Router } from "./infra/Router";
|
|
4
|
-
|
|
5
|
-
// Decorators
|
|
6
|
-
export * from "./decorators";
|
|
7
|
-
|
|
8
|
-
// Middleware
|
|
9
|
-
export * from "./middleware";
|
|
10
|
-
|
|
11
|
-
// Types
|
|
12
|
-
export * from "./types/http/request.type";
|
|
13
|
-
export * from "./types/http/response.type";
|
|
14
|
-
export * from "./types/common.type";
|
|
15
|
-
export * from "./types/routes.type";
|
|
16
|
-
export * from "./types/validations.type";
|
|
17
|
-
|
|
18
|
-
// Configuration
|
|
19
|
-
export * from "./shared/config/ConfigModule";
|
|
20
|
-
|
|
21
|
-
// Utils
|
|
22
|
-
export { logger } from "./utils/Logger";
|
|
23
|
-
export { parseQS } from "./utils/Parser";
|
|
24
|
-
export { HttpError } from "./infra/utils/HttpError";
|
|
25
|
-
export { parseCookiesHeader } from "./utils/cookies/ParserCookie";
|
|
26
|
-
export { serializeCookie } from "./utils/cookies/SerializeCookie";
|
|
27
|
-
export { validateSchema } from "./utils/validators/SchemaValidator";
|
|
1
|
+
// Core Server & Router
|
|
2
|
+
export * from "./infra/Server";
|
|
3
|
+
export { Router } from "./infra/Router";
|
|
4
|
+
|
|
5
|
+
// Decorators
|
|
6
|
+
export * from "./decorators";
|
|
7
|
+
|
|
8
|
+
// Middleware
|
|
9
|
+
export * from "./middleware";
|
|
10
|
+
|
|
11
|
+
// Types
|
|
12
|
+
export * from "./types/http/request.type";
|
|
13
|
+
export * from "./types/http/response.type";
|
|
14
|
+
export * from "./types/common.type";
|
|
15
|
+
export * from "./types/routes.type";
|
|
16
|
+
export * from "./types/validations.type";
|
|
17
|
+
|
|
18
|
+
// Configuration
|
|
19
|
+
export * from "./shared/config/ConfigModule";
|
|
20
|
+
|
|
21
|
+
// Utils
|
|
22
|
+
export { logger } from "./utils/Logger";
|
|
23
|
+
export { parseQS } from "./utils/Parser";
|
|
24
|
+
export { HttpError } from "./infra/utils/HttpError";
|
|
25
|
+
export { parseCookiesHeader } from "./utils/cookies/ParserCookie";
|
|
26
|
+
export { serializeCookie } from "./utils/cookies/SerializeCookie";
|
|
27
|
+
export { validateSchema } from "./utils/validators/SchemaValidator";
|
|
28
28
|
export { validateDto, getDtoValidators } from "./utils/validators/DTOValidator";
|
package/src/infra/Router.ts
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
import { HttpError } from "./utils/HttpError";
|
|
2
|
-
import { Node, type Handler } from "./utils/route/Node";
|
|
3
|
-
|
|
4
|
-
interface MatchResult {
|
|
5
|
-
handlers: Handler[];
|
|
6
|
-
params: Record<string, string>;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export class Router {
|
|
10
|
-
private root = new Node();
|
|
11
|
-
|
|
12
|
-
add(method: string, path: string, ...handlers: Handler[]) {
|
|
13
|
-
const segments = path.split("/").filter(Boolean);
|
|
14
|
-
let node = this.root;
|
|
15
|
-
|
|
16
|
-
for (const seg of segments) {
|
|
17
|
-
let child: Node;
|
|
18
|
-
|
|
19
|
-
if (seg.startsWith(":")) {
|
|
20
|
-
child = new Node();
|
|
21
|
-
child.isParam = true;
|
|
22
|
-
child.paramName = seg.slice(1);
|
|
23
|
-
} else {
|
|
24
|
-
child = node.children.get(seg) ?? new Node();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
node.children.set(seg.startsWith(":") ? ":" : seg, child);
|
|
28
|
-
node = child;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
node.handlers.set(method.toUpperCase(), handlers);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
find(method: string, path: string): MatchResult {
|
|
35
|
-
const segments = path.split("/").filter(Boolean);
|
|
36
|
-
let node = this.root;
|
|
37
|
-
|
|
38
|
-
const params: Record<string, string> = {};
|
|
39
|
-
for (const seg of segments) {
|
|
40
|
-
if (node.children.has(seg)) {
|
|
41
|
-
node = node.children.get(seg)!;
|
|
42
|
-
} else if (node.children.has(":")) {
|
|
43
|
-
node = node.children.get(":")!;
|
|
44
|
-
|
|
45
|
-
if (node.isParam && node.paramName) {
|
|
46
|
-
params[node.paramName] = seg;
|
|
47
|
-
}
|
|
48
|
-
} else {
|
|
49
|
-
throw new HttpError(404, "Route not found");
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const handlers = node.handlers.get(method.toUpperCase()) as Handler[];
|
|
54
|
-
return { handlers, params };
|
|
55
|
-
}
|
|
56
|
-
}
|
|
1
|
+
import { HttpError } from "./utils/HttpError";
|
|
2
|
+
import { Node, type Handler } from "./utils/route/Node";
|
|
3
|
+
|
|
4
|
+
interface MatchResult {
|
|
5
|
+
handlers: Handler[];
|
|
6
|
+
params: Record<string, string>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class Router {
|
|
10
|
+
private root = new Node();
|
|
11
|
+
|
|
12
|
+
add(method: string, path: string, ...handlers: Handler[]) {
|
|
13
|
+
const segments = path.split("/").filter(Boolean);
|
|
14
|
+
let node = this.root;
|
|
15
|
+
|
|
16
|
+
for (const seg of segments) {
|
|
17
|
+
let child: Node;
|
|
18
|
+
|
|
19
|
+
if (seg.startsWith(":")) {
|
|
20
|
+
child = new Node();
|
|
21
|
+
child.isParam = true;
|
|
22
|
+
child.paramName = seg.slice(1);
|
|
23
|
+
} else {
|
|
24
|
+
child = node.children.get(seg) ?? new Node();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
node.children.set(seg.startsWith(":") ? ":" : seg, child);
|
|
28
|
+
node = child;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
node.handlers.set(method.toUpperCase(), handlers);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
find(method: string, path: string): MatchResult {
|
|
35
|
+
const segments = path.split("/").filter(Boolean);
|
|
36
|
+
let node = this.root;
|
|
37
|
+
|
|
38
|
+
const params: Record<string, string> = {};
|
|
39
|
+
for (const seg of segments) {
|
|
40
|
+
if (node.children.has(seg)) {
|
|
41
|
+
node = node.children.get(seg)!;
|
|
42
|
+
} else if (node.children.has(":")) {
|
|
43
|
+
node = node.children.get(":")!;
|
|
44
|
+
|
|
45
|
+
if (node.isParam && node.paramName) {
|
|
46
|
+
params[node.paramName] = seg;
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
throw new HttpError(404, "Route not found");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const handlers = node.handlers.get(method.toUpperCase()) as Handler[];
|
|
54
|
+
return { handlers, params };
|
|
55
|
+
}
|
|
56
|
+
}
|