diesel-core 0.0.1
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/.prettierignore +7 -0
- package/.prettierrc +7 -0
- package/package.json +12 -0
- package/src/ctx.js +192 -0
- package/src/handleRequest.js +63 -0
- package/src/router.js +53 -0
- package/src/server.js +131 -0
- package/src/trie.js +132 -0
package/.prettierignore
ADDED
package/.prettierrc
ADDED
package/package.json
ADDED
package/src/ctx.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
export default function createCtx(req, url) {
|
|
2
|
+
let headers = {};
|
|
3
|
+
let settedValue = {};
|
|
4
|
+
let isAuthenticated = false;
|
|
5
|
+
let parsedQuery = null;
|
|
6
|
+
let parsedCookie = null;
|
|
7
|
+
let parsedParams = null;
|
|
8
|
+
let parsedBody= null
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
req,
|
|
12
|
+
url,
|
|
13
|
+
next: () => {},
|
|
14
|
+
|
|
15
|
+
async body() {
|
|
16
|
+
if (!parsedBody) {
|
|
17
|
+
parsedBody = await parseBody(req)
|
|
18
|
+
return parsedBody;
|
|
19
|
+
}
|
|
20
|
+
return parsedBody;
|
|
21
|
+
},
|
|
22
|
+
setHeader(key, value) {
|
|
23
|
+
headers[key] = value;
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
set(key, value) {
|
|
27
|
+
settedValue[key] = value;
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
get(key) {
|
|
31
|
+
return settedValue[key];
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
setAuth(authStatus) {
|
|
35
|
+
isAuthenticated = authStatus;
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
getAuth() {
|
|
39
|
+
return isAuthenticated;
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
text(data, status = 200) {
|
|
43
|
+
return new Response(data, {
|
|
44
|
+
status,
|
|
45
|
+
headers: headers,
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
json(data, status = 200) {
|
|
50
|
+
return new Response(JSON.stringify(data), {
|
|
51
|
+
status,
|
|
52
|
+
headers: {
|
|
53
|
+
"Content-Type": "application/json",
|
|
54
|
+
...headers,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
html(filepath) {
|
|
60
|
+
return new Response(Bun.file(filepath), {
|
|
61
|
+
status: 200,
|
|
62
|
+
headers: {
|
|
63
|
+
...headers,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
file(filePath) {
|
|
68
|
+
return new Response(Bun.file(filePath), {
|
|
69
|
+
status: 200,
|
|
70
|
+
headers: {
|
|
71
|
+
...headers,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
redirect(path, status = 302) {
|
|
77
|
+
return new Response(null, {
|
|
78
|
+
status,
|
|
79
|
+
headers: {
|
|
80
|
+
Location: path,
|
|
81
|
+
...headers,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
getParams(props) {
|
|
87
|
+
if (!parsedParams) {
|
|
88
|
+
parsedParams = extractDynamicParams(req.routePattern, url.pathname);
|
|
89
|
+
}
|
|
90
|
+
return props ? parsedParams[props] : parsedParams;
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
getQuery(props) {
|
|
94
|
+
if (!parsedQuery) {
|
|
95
|
+
parsedQuery = Object.fromEntries(url.searchParams.entries());
|
|
96
|
+
}
|
|
97
|
+
return props ? parsedQuery[props] : parsedQuery;
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
cookie(name, value, options = {}) {
|
|
101
|
+
let cookieString = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
|
|
102
|
+
|
|
103
|
+
// Add options to cookie string (e.g., expiration, path, HttpOnly, etc.)
|
|
104
|
+
if (options.maxAge) cookieString += `; Max-Age=${options.maxAge}`;
|
|
105
|
+
if (options.expires) cookieString += `; Expires=${options.expires.toUTCString()}`;
|
|
106
|
+
if (options.path) cookieString += `; Path=${options.path}`;
|
|
107
|
+
if (options.domain) cookieString += `; Domain=${options.domain}`;
|
|
108
|
+
if (options.secure) cookieString += `; Secure`;
|
|
109
|
+
if (options.httpOnly) cookieString += `; HttpOnly`;
|
|
110
|
+
if (options.sameSite) cookieString += `; SameSite=${options.sameSite}`;
|
|
111
|
+
|
|
112
|
+
if (headers["Set-Cookie"]) {
|
|
113
|
+
// If it's already an array, push the new cookie, otherwise convert to array
|
|
114
|
+
const existingCookies = Array.isArray(headers["Set-Cookie"]) ? headers["Set-Cookie"] : [headers["Set-Cookie"]];
|
|
115
|
+
|
|
116
|
+
// Add the new cookie string to the array
|
|
117
|
+
existingCookies.push(cookieString);
|
|
118
|
+
|
|
119
|
+
// Update Set-Cookie header
|
|
120
|
+
headers["Set-Cookie"] = existingCookies;
|
|
121
|
+
} else {
|
|
122
|
+
// If no cookies exist, initialize the header
|
|
123
|
+
headers["Set-Cookie"] = cookieString;
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
getCookie(cookieName) {
|
|
127
|
+
if (!parsedCookie) {
|
|
128
|
+
parsedCookie = parseCookie(req.headers.get("cookie"));
|
|
129
|
+
}
|
|
130
|
+
return cookieName ? parsedCookie[cookieName] : parsedCookie;
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function parseCookie(header) {
|
|
136
|
+
const cookies = {};
|
|
137
|
+
if (!header) return cookies;
|
|
138
|
+
|
|
139
|
+
const cookieArray = header.split(";");
|
|
140
|
+
cookieArray.forEach((cookie) => {
|
|
141
|
+
const [cookieName, cookievalue] = cookie.trim().split("=");
|
|
142
|
+
cookies[cookieName] = cookievalue.split(" ")[0];
|
|
143
|
+
});
|
|
144
|
+
return cookies;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const extractDynamicParams = (routePattern, path) => {
|
|
148
|
+
const object = {};
|
|
149
|
+
const routeSegments = routePattern.split("/");
|
|
150
|
+
const [pathWithoutQuery] = path.split("?"); // Ignore the query string in the path
|
|
151
|
+
const pathSegments = pathWithoutQuery.split("/"); // Re-split after removing query
|
|
152
|
+
|
|
153
|
+
if (routeSegments.length !== pathSegments.length) {
|
|
154
|
+
return null; // Path doesn't match the pattern
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
routeSegments.forEach((segment, index) => {
|
|
158
|
+
if (segment.startsWith(":")) {
|
|
159
|
+
const dynamicKey = segment.slice(1); // Remove ':' to get the key name
|
|
160
|
+
object[dynamicKey] = pathSegments[index]; // Map the path segment to the key
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
return object;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
async function parseBody(req) {
|
|
168
|
+
const contentType = req.headers.get("Content-Type");
|
|
169
|
+
if (contentType.includes("application/json")) {
|
|
170
|
+
try {
|
|
171
|
+
return await req.json();
|
|
172
|
+
} catch (error) {
|
|
173
|
+
return new Response({ error: "Invalid JSON format" });
|
|
174
|
+
}
|
|
175
|
+
} else if (contentType.startsWith("application/x-www-form-urlencoded")) {
|
|
176
|
+
const body = await req.text();
|
|
177
|
+
return Object.fromEntries(new URLSearchParams(body));
|
|
178
|
+
} else if (contentType.startsWith("multipart/form-data")) {
|
|
179
|
+
const formData = await req.formData();
|
|
180
|
+
return formDataToObject(formData);
|
|
181
|
+
} else {
|
|
182
|
+
return new Response({ error: "unknown request body" });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function formDataToObject(formData) {
|
|
187
|
+
const obj = {};
|
|
188
|
+
for (const [key, value] of formData.entries()) {
|
|
189
|
+
obj[key] = value;
|
|
190
|
+
}
|
|
191
|
+
return obj;
|
|
192
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import createCtx from "./ctx";
|
|
2
|
+
|
|
3
|
+
export default async function handleRequest(req, url, diesel) {
|
|
4
|
+
const { pathname } = url;
|
|
5
|
+
const { method } = req;
|
|
6
|
+
|
|
7
|
+
// Try to find the route handler in the trie
|
|
8
|
+
const routeHandler = diesel.trie.search(pathname, method);
|
|
9
|
+
|
|
10
|
+
// Early return if route or method is not found
|
|
11
|
+
if (!routeHandler || !routeHandler.handler) return responseNotFound(pathname);
|
|
12
|
+
if (routeHandler.method !== method) return responseMethodNotAllowed();
|
|
13
|
+
|
|
14
|
+
// If the route is dynamic, we only set routePattern if necessary
|
|
15
|
+
if (routeHandler.isDynamic) req.routePattern = routeHandler.path;
|
|
16
|
+
|
|
17
|
+
const ctx = createCtx(req, url);
|
|
18
|
+
|
|
19
|
+
if (diesel.hasMiddleware) {
|
|
20
|
+
const middlewares = [
|
|
21
|
+
...diesel.globalMiddlewares,
|
|
22
|
+
...(diesel.middlewares.get(pathname) || [])
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const middlewareResult = await executeMiddleware(middlewares, ctx);
|
|
26
|
+
if (middlewareResult) return middlewareResult;
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
// Finally, execute the route handler and return its result
|
|
32
|
+
try {
|
|
33
|
+
const result = await routeHandler.handler(ctx);
|
|
34
|
+
return result ?? responseNoHandler();
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return responseServerError();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Optimized middleware execution: stops as soon as a middleware returns a response
|
|
41
|
+
async function executeMiddleware(middlewares, ctx) {
|
|
42
|
+
for (const middleware of middlewares) {
|
|
43
|
+
const result = await middleware(ctx);
|
|
44
|
+
if (result) return result; // Early exit if middleware returns a result
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Reused response functions for better organization and clarity
|
|
49
|
+
function responseNotFound(path) {
|
|
50
|
+
return new Response(`Route not found for ${path}`, { status: 404 });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function responseMethodNotAllowed() {
|
|
54
|
+
return new Response("Method not allowed", { status: 405 });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function responseNoHandler() {
|
|
58
|
+
return new Response("No response from handler", { status: 204 });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function responseServerError() {
|
|
62
|
+
return new Response("Internal Server Error", { status: 500 });
|
|
63
|
+
}
|
package/src/router.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import diesel from "./server";
|
|
2
|
+
|
|
3
|
+
class Router extends diesel{
|
|
4
|
+
constructor(){
|
|
5
|
+
super()
|
|
6
|
+
}
|
|
7
|
+
#addRoute(method,path,handlers){
|
|
8
|
+
|
|
9
|
+
const middlewareHandlers = handlers.slice(0, -1);
|
|
10
|
+
|
|
11
|
+
if (!this.middlewares.has(path)) {
|
|
12
|
+
this.middlewares.set(path,[])
|
|
13
|
+
}
|
|
14
|
+
if (path === '/') {
|
|
15
|
+
middlewareHandlers.forEach(midlleware => {
|
|
16
|
+
if(!this.globalMiddlewares.includes(midlleware)){
|
|
17
|
+
this.globalMiddlewares.push(midlleware)
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
} else {
|
|
21
|
+
if (!this.middlewares.get(path).includes(...middlewareHandlers)) {
|
|
22
|
+
this.middlewares.get(path).push(...middlewareHandlers);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const handler = handlers[handlers.length-1]
|
|
27
|
+
this.trie.insert(path,{handler,method})
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
get(path,...handlers){
|
|
31
|
+
return this.#addRoute("GET",path,handlers)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
post(path,...handlers){
|
|
35
|
+
return this.#addRoute("POST",path,handlers)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
put(path,...handlers){
|
|
39
|
+
return this.#addRoute("PUT",path,handlers)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
patch(path,...handlers){
|
|
43
|
+
if (handlers.length>0) {
|
|
44
|
+
return this.#addRoute("PATCH",path,handlers)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
delete(path,...handlers){
|
|
49
|
+
return this.#addRoute("DELETE",path,handlers)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default Router
|
package/src/server.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {serve} from 'bun'
|
|
2
|
+
import Trie from './trie.js';
|
|
3
|
+
import handleRequest from './handleRequest.js'
|
|
4
|
+
|
|
5
|
+
class diesel {
|
|
6
|
+
constructor(){
|
|
7
|
+
this.routes = new Map()
|
|
8
|
+
this.globalMiddlewares = [];
|
|
9
|
+
this.middlewares = new Map()
|
|
10
|
+
this.trie = new Trie()
|
|
11
|
+
this.hasMiddleware = false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
compile (){
|
|
15
|
+
if (this.globalMiddlewares.length > 0) {
|
|
16
|
+
this.hasMiddleware = true;
|
|
17
|
+
}
|
|
18
|
+
for (const [path, middlewares] of this.middlewares.entries()) {
|
|
19
|
+
if (middlewares.length > 0) {
|
|
20
|
+
this.hasMiddleware = true;
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
listen(port,callback){
|
|
27
|
+
this.compile()
|
|
28
|
+
const server = serve({
|
|
29
|
+
port,
|
|
30
|
+
fetch: (req) => {
|
|
31
|
+
const url = new URL(req.url)
|
|
32
|
+
return handleRequest(req,url,this)
|
|
33
|
+
},
|
|
34
|
+
onClose() {
|
|
35
|
+
console.log("Server is shutting down...");
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
if (typeof callback === 'function') {
|
|
39
|
+
return callback();
|
|
40
|
+
}
|
|
41
|
+
console.log(`Server is running on http://localhost:${port}`);
|
|
42
|
+
return server;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
register(pathPrefix,handlerInstance){
|
|
46
|
+
const routeEntries = Object.entries(handlerInstance.trie.root.children);
|
|
47
|
+
handlerInstance.trie.root.subMiddlewares.forEach((middleware,path)=>{
|
|
48
|
+
if (!this.middlewares.has(pathPrefix+path)) {
|
|
49
|
+
this.middlewares.set(pathPrefix+path, []);
|
|
50
|
+
}
|
|
51
|
+
if (!this.middlewares.get(pathPrefix+path).includes(...middleware)) {
|
|
52
|
+
this.middlewares.get(pathPrefix+path).push(...middleware);
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
for (const [routeKey, routeNode] of routeEntries) {
|
|
56
|
+
const fullpath = pathPrefix + routeNode?.path;
|
|
57
|
+
const routeHandler = routeNode.handler[0];
|
|
58
|
+
const httpMethod = routeNode.method[0];
|
|
59
|
+
this.trie.insert(fullpath, { handler: routeHandler, method: httpMethod });
|
|
60
|
+
}
|
|
61
|
+
handlerInstance.trie = new Trie();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#addRoute(method,path,handlers){
|
|
65
|
+
|
|
66
|
+
const middlewareHandlers = handlers.slice(0, -1);
|
|
67
|
+
|
|
68
|
+
if (!this.middlewares.has(path)) {
|
|
69
|
+
this.middlewares.set(path,[])
|
|
70
|
+
}
|
|
71
|
+
if (path === '/') {
|
|
72
|
+
middlewareHandlers.forEach(midlleware => {
|
|
73
|
+
if(!this.globalMiddlewares.includes(midlleware)){
|
|
74
|
+
this.globalMiddlewares.push(midlleware)
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
} else {
|
|
78
|
+
if (!this.middlewares.get(path).includes(...middlewareHandlers)) {
|
|
79
|
+
this.middlewares.get(path).push(...middlewareHandlers);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const handler = handlers[handlers.length-1]
|
|
84
|
+
this.trie.insert(path,{handler,method})
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
use(pathORHandler,handler){
|
|
90
|
+
if (typeof pathORHandler === 'function') {
|
|
91
|
+
if (!this.globalMiddlewares.includes(pathORHandler)) {
|
|
92
|
+
return this.globalMiddlewares.push(pathORHandler)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// now it means it is path midl
|
|
96
|
+
const path = pathORHandler
|
|
97
|
+
if (!this.middlewares.has(path)) {
|
|
98
|
+
this.middlewares.set(path,[])
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if(!this.middlewares.get(path).includes(handler)){
|
|
102
|
+
this.middlewares.get(path).push(handler)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get(path,...handlers){
|
|
107
|
+
return this.#addRoute("GET",path,handlers)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
post(path,...handlers){
|
|
111
|
+
return this.#addRoute("POST",path,handlers)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
put(path,...handlers){
|
|
115
|
+
return this.#addRoute("PUT",path,handlers)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
patch(path,...handlers){
|
|
119
|
+
if (handlers.length>0) {
|
|
120
|
+
return this.#addRoute("PATCH",path,handlers)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
delete(path,...handlers){
|
|
125
|
+
return this.#addRoute("DELETE",path,handlers)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
export default diesel;
|
package/src/trie.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
class TrieNode {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.children = {};
|
|
4
|
+
this.isEndOfWord = false;
|
|
5
|
+
this.handler = [];
|
|
6
|
+
this.isImportant = false;
|
|
7
|
+
this.isDynamic = false;
|
|
8
|
+
this.pattern = '';
|
|
9
|
+
this.path = "";
|
|
10
|
+
this.method = []
|
|
11
|
+
this.subMiddlewares= new Map()
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default class Trie {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.root = new TrieNode();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
insert(path, route) {
|
|
21
|
+
let node = this.root;
|
|
22
|
+
const pathSegments = path.split('/').filter(Boolean); // Split path by segments
|
|
23
|
+
|
|
24
|
+
// If it's the root path '/', treat it separately
|
|
25
|
+
if (path === '/') {
|
|
26
|
+
node.isEndOfWord = true;
|
|
27
|
+
node.handler.push(route.handler)
|
|
28
|
+
node.isImportant = route.isImportant;
|
|
29
|
+
node.path = path;
|
|
30
|
+
node.method.push(route.method)
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const segment of pathSegments) {
|
|
35
|
+
let isDynamic = false;
|
|
36
|
+
let key = segment;
|
|
37
|
+
|
|
38
|
+
if (segment.startsWith(':')) {
|
|
39
|
+
isDynamic = true;
|
|
40
|
+
key = ':'; // Store dynamic routes under the key ':'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!node.children[key]) {
|
|
44
|
+
node.children[key] = new TrieNode();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
node = node.children[key];
|
|
48
|
+
// Set dynamic route information if applicable
|
|
49
|
+
node.isDynamic = isDynamic;
|
|
50
|
+
node.pattern = segment; // Store the actual pattern like ':id'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// After looping through the entire path, assign route details
|
|
54
|
+
node.isEndOfWord = true;
|
|
55
|
+
node.method.push(route.method);
|
|
56
|
+
node.handler.push(route.handler)
|
|
57
|
+
node.isImportant = route.isImportant;
|
|
58
|
+
node.path = path; // Store the original path
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
insertMidl(midl){
|
|
62
|
+
if (!this.root.subMiddlewares.has(midl)) {
|
|
63
|
+
this.root.subMiddlewares.set(midl)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
search(path, method) {
|
|
69
|
+
let node = this.root;
|
|
70
|
+
const pathSegments = path.split('/').filter(Boolean); // Split path into segments
|
|
71
|
+
|
|
72
|
+
for (const segment of pathSegments) {
|
|
73
|
+
let key = segment;
|
|
74
|
+
|
|
75
|
+
// Check for exact match first (static)
|
|
76
|
+
if (!node.children[key]) {
|
|
77
|
+
// Try dynamic match (e.g., ':id')
|
|
78
|
+
if (node.children[':']) {
|
|
79
|
+
node = node.children[':'];
|
|
80
|
+
} else {
|
|
81
|
+
return null; // No match
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
node = node.children[key];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Method matching
|
|
89
|
+
let routeMethodIndex = node.method.indexOf(method); // More efficient method match
|
|
90
|
+
if (routeMethodIndex !== -1) {
|
|
91
|
+
return {
|
|
92
|
+
path: node.path,
|
|
93
|
+
handler: node.handler[routeMethodIndex],
|
|
94
|
+
isDynamic: node.isDynamic,
|
|
95
|
+
pattern: node.pattern,
|
|
96
|
+
method: node.method[routeMethodIndex]
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Fallback if method is not found
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
// New getAllRoutes method
|
|
107
|
+
|
|
108
|
+
getAllRoutes() {
|
|
109
|
+
const routes = [];
|
|
110
|
+
// Helper function to recursively collect all routes
|
|
111
|
+
const traverse = (node, currentPath) => {
|
|
112
|
+
if (node.isEndOfWord) {
|
|
113
|
+
routes.push({
|
|
114
|
+
path: currentPath,
|
|
115
|
+
handler: node.handler,
|
|
116
|
+
isImportant: node.isImportant,
|
|
117
|
+
isDynamic: node.isDynamic,
|
|
118
|
+
pattern: node.pattern,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// Recursively traverse all children
|
|
122
|
+
for (const key in node.children) {
|
|
123
|
+
const child = node.children[key];
|
|
124
|
+
const newPath = currentPath + (key === ':' ? '/:' + child.pattern : '/' + key); // Reconstruct the full path
|
|
125
|
+
traverse(child, newPath);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
// Start traversal from the root
|
|
129
|
+
traverse(this.root, "");
|
|
130
|
+
return routes;
|
|
131
|
+
}
|
|
132
|
+
}
|