diesel-core 0.0.4 → 0.0.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/LICENSE +8 -0
- package/README.md +56 -0
- package/build.js +12 -0
- package/dist/ctx.js +1 -0
- package/dist/handleRequest.js +1 -0
- package/dist/router.js +1 -0
- package/dist/server.js +1 -0
- package/dist/trie.js +1 -0
- package/package.json +2 -2
- package/src/ctx.js +78 -45
- package/src/handleRequest.js +42 -20
- package/src/server.js +170 -112
- package/src/trie.js +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Diesel Bunjs License
|
|
2
|
+
Copyright (c) 2024 Pradeep Sahu
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
5
|
+
|
|
6
|
+
1. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
7
|
+
|
|
8
|
+
2. The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non-infringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the Software or the use or other dealings in the Software.
|
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# DieselJS
|
|
2
|
+
|
|
3
|
+
**Diesel** is a simple and lightweight HTTP server library for Bunjs, designed to give you control over your API routes and middleware in an intuitive and efficient way. With DieselJS, you can quickly set up a server, define routes, and optimize important routes for faster response times.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
Install diesel-core via bun, npm , yarn , pnpm :
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install diesel-core
|
|
12
|
+
|
|
13
|
+
import diesel from "diesel-core";
|
|
14
|
+
import { hello } from "./hello.js";
|
|
15
|
+
|
|
16
|
+
const maya = new diesel();
|
|
17
|
+
const port = 3000;
|
|
18
|
+
|
|
19
|
+
// Middleware example
|
|
20
|
+
app.use(hello);
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
app.get("/", async (xl) => {
|
|
24
|
+
return xl.status(200).text("Hello world...!")
|
|
25
|
+
OR
|
|
26
|
+
return xl.text("Hello world!")
|
|
27
|
+
OR
|
|
28
|
+
return new Response("Hello world")
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Render a HTML page
|
|
32
|
+
app.get("/render",async (xl) => {
|
|
33
|
+
return xl.html(`${import.meta.dir}/index.html`);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
app.get("/async-test",async (xl) => {
|
|
37
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
38
|
+
return xl.json({ message: "Async operation completed" },200);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
app.get("/redirect",(xl) => {
|
|
43
|
+
return xl.redirect("/");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
app.get("/hello/:id",(xl) => {
|
|
47
|
+
const id = xl.getParams("id")
|
|
48
|
+
return xl.json({ msg: "Hello", id });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
// Start the server
|
|
53
|
+
app.listen(port, () => {
|
|
54
|
+
console.log(`diesel is running on port ${port}`);
|
|
55
|
+
});
|
|
56
|
+
|
package/build.js
ADDED
package/dist/ctx.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
async function H(G){const F={};if(!G)return F;return G.split(";").forEach((L)=>{const[X,M]=L.trim().split("=");F[X]=M.split(" ")[0]}),F}function K(G,F){const z={},L=G.split("/"),[X]=F.split("?"),M=X.split("/");if(L.length!==M.length)return null;return L.forEach((U,Y)=>{if(U.startsWith(":")){const Z=U.slice(1);z[Z]=M[Y]}}),z}async function W(G){const F=G.headers.get("Content-Type")||"";if(!F)return{};try{if(F.startsWith("application/json"))return await G.json();if(F.startsWith("application/x-www-form-urlencoded")){const z=await G.text();return Object.fromEntries(new URLSearchParams(z))}if(F.startsWith("multipart/form-data")){const z=await G.formData();return O(z)}return new Response({error:"Unknown request body type"})}catch(z){return new Response({error:"Invalid request body format"})}}function O(G){const F={};for(let[z,L]of G.entries())F[z]=L;return F}function $(G,F){let z={},L={},X=!1,M=null,U=null,Y=null,Z=null,J=200;return{req:G,url:F,next:()=>{},status(w){return J=w,this},async body(){if(!Z)Z=await W(G);return Z},setHeader(w,E){return z[w]=E,this},set(w,E){return L[w]=E,this},get(w){return L[w]},setAuth(w){return X=w,this},getAuth(){return X},text(w,E){if(E)J=E;return new Response(w,{status:J,headers:z})},json(w,E){if(E)J=E;return new Response(JSON.stringify(w),{status:J,headers:{"Content-Type":"application/json",...z}})},html(w,E){if(E)J=E;return new Response(Bun.file(w),{status:J,headers:{...z}})},file(w,E){if(E)J=E;return new Response(Bun.file(w),{status:J,headers:{...z}})},redirect(w,E){if(E)J=E;return new Response(null,{status:J,headers:{Location:w,...z}})},getParams(w){if(!Y)Y=K(G.routePattern,F.pathname);return w?Y[w]:Y},getQuery(w){if(!M)M=Object.fromEntries(F.searchParams);return w?M[w]:M},async cookie(w,E,I={}){let R=`${encodeURIComponent(w)}=${encodeURIComponent(E)}`;if(I.maxAge)R+=`; Max-Age=${I.maxAge}`;if(I.expires)R+=`; Expires=${I.expires.toUTCString()}`;if(I.path)R+=`; Path=${I.path}`;if(I.domain)R+=`; Domain=${I.domain}`;if(I.secure)R+="; Secure";if(I.httpOnly)R+="; HttpOnly";if(I.sameSite)R+=`; SameSite=${I.sameSite}`;if(z["Set-Cookie"]){const _=Array.isArray(z["Set-Cookie"])?z["Set-Cookie"]:[z["Set-Cookie"]];_.push(R),z["Set-Cookie"]=_}else z["Set-Cookie"]=R;return this},async getCookie(w){if(!U)U=await H(G.headers.get("cookie"));return w?U[w]:U}}}export{$ as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
async function O(J){const L={};if(!J)return L;return J.split(";").forEach((X)=>{const[_,Y]=X.trim().split("=");L[_]=Y.split(" ")[0]}),L}function V(J,L){const G={},X=J.split("/"),[_]=L.split("?"),Y=_.split("/");if(X.length!==Y.length)return null;return X.forEach((Z,E)=>{if(Z.startsWith(":")){const K=Z.slice(1);G[K]=Y[E]}}),G}async function A(J){const L=J.headers.get("Content-Type")||"";if(!L)return{};try{if(L.startsWith("application/json"))return await J.json();if(L.startsWith("application/x-www-form-urlencoded")){const G=await J.text();return Object.fromEntries(new URLSearchParams(G))}if(L.startsWith("multipart/form-data")){const G=await J.formData();return B(G)}return new Response({error:"Unknown request body type"})}catch(G){return new Response({error:"Invalid request body format"})}}function B(J){const L={};for(let[G,X]of J.entries())L[G]=X;return L}function W(J,L){let G={},X={},_=!1,Y=null,Z=null,E=null,K=null,z=200;return{req:J,url:L,next:()=>{},status(I){return z=I,this},async body(){if(!K)K=await A(J);return K},setHeader(I,U){return G[I]=U,this},set(I,U){return X[I]=U,this},get(I){return X[I]},setAuth(I){return _=I,this},getAuth(){return _},text(I,U){if(U)z=U;return new Response(I,{status:z,headers:G})},json(I,U){if(U)z=U;return new Response(JSON.stringify(I),{status:z,headers:{"Content-Type":"application/json",...G}})},html(I,U){if(U)z=U;return new Response(Bun.file(I),{status:z,headers:{...G}})},file(I,U){if(U)z=U;return new Response(Bun.file(I),{status:z,headers:{...G}})},redirect(I,U){if(U)z=U;return new Response(null,{status:z,headers:{Location:I,...G}})},getParams(I){if(!E)E=V(J.routePattern,L.pathname);return I?E[I]:E},getQuery(I){if(!Y)Y=Object.fromEntries(L.searchParams);return I?Y[I]:Y},async cookie(I,U,$={}){let F=`${encodeURIComponent(I)}=${encodeURIComponent(U)}`;if($.maxAge)F+=`; Max-Age=${$.maxAge}`;if($.expires)F+=`; Expires=${$.expires.toUTCString()}`;if($.path)F+=`; Path=${$.path}`;if($.domain)F+=`; Domain=${$.domain}`;if($.secure)F+="; Secure";if($.httpOnly)F+="; HttpOnly";if($.sameSite)F+=`; SameSite=${$.sameSite}`;if(G["Set-Cookie"]){const M=Array.isArray(G["Set-Cookie"])?G["Set-Cookie"]:[G["Set-Cookie"]];M.push(F),G["Set-Cookie"]=M}else G["Set-Cookie"]=F;return this},async getCookie(I){if(!Z)Z=await O(J.headers.get("cookie"));return I?Z[I]:Z}}}async function D(J,L){for(let G of J){const X=await G(L);if(X)return X}}function Q(J){return new Response(`Route not found for ${J}`,{status:404})}function R(){return new Response("Method not allowed",{status:405})}function T(){return new Response("No response from handler",{status:204})}function b(){return new Response("Internal Server Error",{status:500})}async function j(J,L,G){const X=W(J,L);if(G.hasOnReqHook)for(let Y of G.hooks.onRequest)await Y(X);if(G.hasMiddleware){const Y=[...G.globalMiddlewares,...G.middlewares.get(L.pathname)||[]],Z=await D(Y,X);if(Z)return Z}const _=G.trie.search(L.pathname,J.method);if(!_||!_.handler)return Q(L.pathname);if(_.method!==J.method)return R();if(_.isDynamic)J.routePattern=_.path;if(G.hasPreHandlerHook)for(let Y of G.hooks.preHandler){const Z=await Y(X);if(Z)return Z}try{const Y=await _.handler(X);if(G.hasPostHandlerHook)for(let Z of G.hooks.postHandler)await Z(X);if(G.hasOnSendHook)for(let Z of G.hooks.onSend){const E=await Z(Y,X);if(E)return E}return Y??T()}catch(Y){return b()}}export{j as default};
|
package/dist/router.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class V{constructor(){this.children={},this.isEndOfWord=!1,this.handler=[],this.isDynamic=!1,this.pattern="",this.path="",this.method=[],this.subMiddlewares=new Map}}class M{constructor(){this.root=new V}insert(L,G){let E=this.root;const J=L.split("/").filter(Boolean);if(L==="/"){E.isEndOfWord=!0,E.handler.push(G.handler),E.path=L,E.method.push(G.method);return}for(let U of J){let X=!1,Y=U;if(U.startsWith(":"))X=!0,Y=":";if(!E.children[Y])E.children[Y]=new V;E=E.children[Y],E.isDynamic=X,E.pattern=U}E.isEndOfWord=!0,E.method.push(G.method),E.handler.push(G.handler),E.path=L}insertMidl(L){if(!this.root.subMiddlewares.has(L))this.root.subMiddlewares.set(L)}search(L,G){let E=this.root;const J=L.split("/").filter(Boolean);for(let X of J){let Y=X;if(!E.children[Y])if(E.children[":"])E=E.children[":"];else return null;else E=E.children[Y]}let U=E.method.indexOf(G);if(U!==-1)return{path:E.path,handler:E.handler[U],isDynamic:E.isDynamic,pattern:E.pattern,method:E.method[U]};return{path:E.path,handler:E.handler,isDynamic:E.isDynamic,pattern:E.pattern,method:E.method[U]}}getAllRoutes(){const L=[],G=(E,J)=>{if(E.isEndOfWord)L.push({path:J,handler:E.handler,isImportant:E.isImportant,isDynamic:E.isDynamic,pattern:E.pattern});for(let U in E.children){const X=E.children[U],Y=J+(U===":"?"/:"+X.pattern:"/"+U);G(X,Y)}};return G(this.root,""),L}}async function K(L){const G={};if(!L)return G;return L.split(";").forEach((J)=>{const[U,X]=J.trim().split("=");G[U]=X.split(" ")[0]}),G}function I(L,G){const E={},J=L.split("/"),[U]=G.split("?"),X=U.split("/");if(J.length!==X.length)return null;return J.forEach((Y,$)=>{if(Y.startsWith(":")){const A=Y.slice(1);E[A]=X[$]}}),E}async function v(L){const G=L.headers.get("Content-Type")||"";if(!G)return{};try{if(G.startsWith("application/json"))return await L.json();if(G.startsWith("application/x-www-form-urlencoded")){const E=await L.text();return Object.fromEntries(new URLSearchParams(E))}if(G.startsWith("multipart/form-data")){const E=await L.formData();return C(E)}return new Response({error:"Unknown request body type"})}catch(E){return new Response({error:"Invalid request body format"})}}function C(L){const G={};for(let[E,J]of L.entries())G[E]=J;return G}function B(L,G){let E={},J={},U=!1,X=null,Y=null,$=null,A=null,F=200;return{req:L,url:G,next:()=>{},status(Z){return F=Z,this},async body(){if(!A)A=await v(L);return A},setHeader(Z,_){return E[Z]=_,this},set(Z,_){return J[Z]=_,this},get(Z){return J[Z]},setAuth(Z){return U=Z,this},getAuth(){return U},text(Z,_){if(_)F=_;return new Response(Z,{status:F,headers:E})},json(Z,_){if(_)F=_;return new Response(JSON.stringify(Z),{status:F,headers:{"Content-Type":"application/json",...E}})},html(Z,_){if(_)F=_;return new Response(Bun.file(Z),{status:F,headers:{...E}})},file(Z,_){if(_)F=_;return new Response(Bun.file(Z),{status:F,headers:{...E}})},redirect(Z,_){if(_)F=_;return new Response(null,{status:F,headers:{Location:Z,...E}})},getParams(Z){if(!$)$=I(L.routePattern,G.pathname);return Z?$[Z]:$},getQuery(Z){if(!X)X=Object.fromEntries(G.searchParams);return Z?X[Z]:X},async cookie(Z,_,z={}){let W=`${encodeURIComponent(Z)}=${encodeURIComponent(_)}`;if(z.maxAge)W+=`; Max-Age=${z.maxAge}`;if(z.expires)W+=`; Expires=${z.expires.toUTCString()}`;if(z.path)W+=`; Path=${z.path}`;if(z.domain)W+=`; Domain=${z.domain}`;if(z.secure)W+="; Secure";if(z.httpOnly)W+="; HttpOnly";if(z.sameSite)W+=`; SameSite=${z.sameSite}`;if(E["Set-Cookie"]){const D=Array.isArray(E["Set-Cookie"])?E["Set-Cookie"]:[E["Set-Cookie"]];D.push(W),E["Set-Cookie"]=D}else E["Set-Cookie"]=W;return this},async getCookie(Z){if(!Y)Y=await K(L.headers.get("cookie"));return Z?Y[Z]:Y}}}async function O(L,G){for(let E of L){const J=await E(G);if(J)return J}}function N(L){return new Response(`Route not found for ${L}`,{status:404})}function w(){return new Response("Method not allowed",{status:405})}function R(){return new Response("No response from handler",{status:204})}function P(){return new Response("Internal Server Error",{status:500})}async function Q(L,G,E){const J=B(L,G);if(E.hasOnReqHook)for(let X of E.hooks.onRequest)await X(J);if(E.hasMiddleware){const X=[...E.globalMiddlewares,...E.middlewares.get(G.pathname)||[]],Y=await O(X,J);if(Y)return Y}const U=E.trie.search(G.pathname,L.method);if(!U||!U.handler)return N(G.pathname);if(U.method!==L.method)return w();if(U.isDynamic)L.routePattern=U.path;if(E.hasPreHandlerHook)for(let X of E.hooks.preHandler){const Y=await X(J);if(Y)return Y}try{const X=await U.handler(J);if(E.hasPostHandlerHook)for(let Y of E.hooks.postHandler)await Y(J);if(E.hasOnSendHook)for(let Y of E.hooks.onSend){const $=await Y(X,J);if($)return $}return X??R()}catch(X){return P()}}class T{constructor(){this.routes=new Map,this.globalMiddlewares=[],this.middlewares=new Map,this.trie=new M,this.hasMiddleware=!1,this.hasOnReqHook=!1,this.hasPreHandlerHook=!1,this.hasPostHandlerHook=!1,this.hasOnSendHook=!1,this.hooks={onRequest:[],preHandler:[],postHandler:[],onSend:[],onError:[],onClose:[]}}addHooks(L,G){if(this.hooks[L])this.hooks[L].push(G);else throw new Error(`Unknown hook type: ${type}`)}compile(){if(this.globalMiddlewares.length>0)this.hasMiddleware=!0;for(let[L,G]of this.middlewares.entries())if(G.length>0){this.hasMiddleware=!0;break}if(this.hooks.onRequest.length>0)this.hasOnReqHook=!0;if(this.hooks.preHandler.length>0)this.hasPreHandlerHook=!0;if(this.hooks.postHandler.length>0)this.hasPostHandlerHook=!0;if(this.hooks.onSend.length>0)this.hasOnSendHook=!0}listen(L,{sslCert:G=null,sslKey:E=null}={},J){this.compile();const U={port:L,fetch:async(Y)=>{const $=new URL(Y.url);try{return await Q(Y,$,this)}catch(A){return new Response("Internal Server Error",{status:500})}},onClose(){console.log("Server is shutting down...")}};if(G&&E)U.certFile=G,U.keyFile=E;const X=Bun.serve(U);if(typeof J==="function")return J();if(G&&E)console.log(`HTTPS server is running on https://localhost:${L}`);else console.log(`HTTP server is running on http://localhost:${L}`);return X}register(L,G){const E=Object.entries(G.trie.root.children);G.trie.root.subMiddlewares.forEach((J,U)=>{if(!this.middlewares.has(L+U))this.middlewares.set(L+U,[]);if(!this.middlewares.get(L+U).includes(...J))this.middlewares.get(L+U).push(...J)});for(let[J,U]of E){const X=L+U?.path,Y=U.handler[0],$=U.method[0];this.trie.insert(X,{handler:Y,method:$})}G.trie=new M}#E(L,G,E){const J=E.slice(0,-1);if(!this.middlewares.has(G))this.middlewares.set(G,[]);if(G==="/")J.forEach((X)=>{if(!this.globalMiddlewares.includes(X))this.globalMiddlewares.push(X)});else if(!this.middlewares.get(G).includes(...J))this.middlewares.get(G).push(...J);const U=E[E.length-1];this.trie.insert(G,{handler:U,method:L})}use(L,G){if(typeof L==="function"){if(!this.globalMiddlewares.includes(L))return this.globalMiddlewares.push(L)}const E=L;if(!this.middlewares.has(E))this.middlewares.set(E,[]);if(!this.middlewares.get(E).includes(G))this.middlewares.get(E).push(G)}get(L,...G){return this.#E("GET",L,G)}post(L,...G){return this.#E("POST",L,G)}put(L,...G){return this.#E("PUT",L,G)}patch(L,...G){if(G.length>0)return this.#E("PATCH",L,G)}delete(L,...G){return this.#E("DELETE",L,G)}}var b=T;class j extends b{constructor(){super()}#E(L,G,E){if(!this.trie.root.subMiddlewares.has(G))this.trie.root.subMiddlewares.set(G,[]);const J=E.slice(0,-1);if(!this.trie.root.subMiddlewares.get(G).includes(...J))this.trie.root.subMiddlewares.get(G).push(...J);const U=E[E.length-1];this.trie.insert(G,{handler:U,method:L})}get(L,...G){return this.#E("GET",L,G)}post(L,...G){return this.#E("POST",L,G)}put(L,...G){return this.#E("PUT",L,G)}patch(L,...G){if(G.length>0)return this.#E("PATCH",L,G)}delete(L,...G){return this.#E("DELETE",L,G)}}var c=j;export{c as default};
|
package/dist/server.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class B{constructor(){this.children={},this.isEndOfWord=!1,this.handler=[],this.isDynamic=!1,this.pattern="",this.path="",this.method=[],this.subMiddlewares=new Map}}class A{constructor(){this.root=new B}insert(J,L){let G=this.root;const X=J.split("/").filter(Boolean);if(J==="/"){G.isEndOfWord=!0,G.handler.push(L.handler),G.path=J,G.method.push(L.method);return}for(let U of X){let Y=!1,Z=U;if(U.startsWith(":"))Y=!0,Z=":";if(!G.children[Z])G.children[Z]=new B;G=G.children[Z],G.isDynamic=Y,G.pattern=U}G.isEndOfWord=!0,G.method.push(L.method),G.handler.push(L.handler),G.path=J}insertMidl(J){if(!this.root.subMiddlewares.has(J))this.root.subMiddlewares.set(J)}search(J,L){let G=this.root;const X=J.split("/").filter(Boolean);for(let Y of X){let Z=Y;if(!G.children[Z])if(G.children[":"])G=G.children[":"];else return null;else G=G.children[Z]}let U=G.method.indexOf(L);if(U!==-1)return{path:G.path,handler:G.handler[U],isDynamic:G.isDynamic,pattern:G.pattern,method:G.method[U]};return{path:G.path,handler:G.handler,isDynamic:G.isDynamic,pattern:G.pattern,method:G.method[U]}}getAllRoutes(){const J=[],L=(G,X)=>{if(G.isEndOfWord)J.push({path:X,handler:G.handler,isImportant:G.isImportant,isDynamic:G.isDynamic,pattern:G.pattern});for(let U in G.children){const Y=G.children[U],Z=X+(U===":"?"/:"+Y.pattern:"/"+U);L(Y,Z)}};return L(this.root,""),J}}async function M(J){const L={};if(!J)return L;return J.split(";").forEach((X)=>{const[U,Y]=X.trim().split("=");L[U]=Y.split(" ")[0]}),L}function b(J,L){const G={},X=J.split("/"),[U]=L.split("?"),Y=U.split("/");if(X.length!==Y.length)return null;return X.forEach((Z,z)=>{if(Z.startsWith(":")){const V=Z.slice(1);G[V]=Y[z]}}),G}async function I(J){const L=J.headers.get("Content-Type")||"";if(!L)return{};try{if(L.startsWith("application/json"))return await J.json();if(L.startsWith("application/x-www-form-urlencoded")){const G=await J.text();return Object.fromEntries(new URLSearchParams(G))}if(L.startsWith("multipart/form-data")){const G=await J.formData();return T(G)}return new Response({error:"Unknown request body type"})}catch(G){return new Response({error:"Invalid request body format"})}}function T(J){const L={};for(let[G,X]of J.entries())L[G]=X;return L}function Q(J,L){let G={},X={},U=!1,Y=null,Z=null,z=null,V=null,W=200;return{req:J,url:L,next:()=>{},status(_){return W=_,this},async body(){if(!V)V=await I(J);return V},setHeader(_,$){return G[_]=$,this},set(_,$){return X[_]=$,this},get(_){return X[_]},setAuth(_){return U=_,this},getAuth(){return U},text(_,$){if($)W=$;return new Response(_,{status:W,headers:G})},json(_,$){if($)W=$;return new Response(JSON.stringify(_),{status:W,headers:{"Content-Type":"application/json",...G}})},html(_,$){if($)W=$;return new Response(Bun.file(_),{status:W,headers:{...G}})},file(_,$){if($)W=$;return new Response(Bun.file(_),{status:W,headers:{...G}})},redirect(_,$){if($)W=$;return new Response(null,{status:W,headers:{Location:_,...G}})},getParams(_){if(!z)z=b(J.routePattern,L.pathname);return _?z[_]:z},getQuery(_){if(!Y)Y=Object.fromEntries(L.searchParams);return _?Y[_]:Y},async cookie(_,$,F={}){let E=`${encodeURIComponent(_)}=${encodeURIComponent($)}`;if(F.maxAge)E+=`; Max-Age=${F.maxAge}`;if(F.expires)E+=`; Expires=${F.expires.toUTCString()}`;if(F.path)E+=`; Path=${F.path}`;if(F.domain)E+=`; Domain=${F.domain}`;if(F.secure)E+="; Secure";if(F.httpOnly)E+="; HttpOnly";if(F.sameSite)E+=`; SameSite=${F.sameSite}`;if(G["Set-Cookie"]){const D=Array.isArray(G["Set-Cookie"])?G["Set-Cookie"]:[G["Set-Cookie"]];D.push(E),G["Set-Cookie"]=D}else G["Set-Cookie"]=E;return this},async getCookie(_){if(!Z)Z=await M(J.headers.get("cookie"));return _?Z[_]:Z}}}async function v(J,L){for(let G of J){const X=await G(L);if(X)return X}}function C(J){return new Response(`Route not found for ${J}`,{status:404})}function O(){return new Response("Method not allowed",{status:405})}function N(){return new Response("No response from handler",{status:204})}function R(){return new Response("Internal Server Error",{status:500})}async function j(J,L,G){const X=Q(J,L);if(G.hasOnReqHook)for(let Y of G.hooks.onRequest)await Y(X);if(G.hasMiddleware){const Y=[...G.globalMiddlewares,...G.middlewares.get(L.pathname)||[]],Z=await v(Y,X);if(Z)return Z}const U=G.trie.search(L.pathname,J.method);if(!U||!U.handler)return C(L.pathname);if(U.method!==J.method)return O();if(U.isDynamic)J.routePattern=U.path;if(G.hasPreHandlerHook)for(let Y of G.hooks.preHandler){const Z=await Y(X);if(Z)return Z}try{const Y=await U.handler(X);if(G.hasPostHandlerHook)for(let Z of G.hooks.postHandler)await Z(X);if(G.hasOnSendHook)for(let Z of G.hooks.onSend){const z=await Z(Y,X);if(z)return z}return Y??N()}catch(Y){return R()}}class K{constructor(){this.routes=new Map,this.globalMiddlewares=[],this.middlewares=new Map,this.trie=new A,this.hasMiddleware=!1,this.hasOnReqHook=!1,this.hasPreHandlerHook=!1,this.hasPostHandlerHook=!1,this.hasOnSendHook=!1,this.hooks={onRequest:[],preHandler:[],postHandler:[],onSend:[],onError:[],onClose:[]}}addHooks(J,L){if(this.hooks[J])this.hooks[J].push(L);else throw new Error(`Unknown hook type: ${type}`)}compile(){if(this.globalMiddlewares.length>0)this.hasMiddleware=!0;for(let[J,L]of this.middlewares.entries())if(L.length>0){this.hasMiddleware=!0;break}if(this.hooks.onRequest.length>0)this.hasOnReqHook=!0;if(this.hooks.preHandler.length>0)this.hasPreHandlerHook=!0;if(this.hooks.postHandler.length>0)this.hasPostHandlerHook=!0;if(this.hooks.onSend.length>0)this.hasOnSendHook=!0}listen(J,{sslCert:L=null,sslKey:G=null}={},X){this.compile();const U={port:J,fetch:async(Z)=>{const z=new URL(Z.url);try{return await j(Z,z,this)}catch(V){return new Response("Internal Server Error",{status:500})}},onClose(){console.log("Server is shutting down...")}};if(L&&G)U.certFile=L,U.keyFile=G;const Y=Bun.serve(U);if(typeof X==="function")return X();if(L&&G)console.log(`HTTPS server is running on https://localhost:${J}`);else console.log(`HTTP server is running on http://localhost:${J}`);return Y}register(J,L){const G=Object.entries(L.trie.root.children);L.trie.root.subMiddlewares.forEach((X,U)=>{if(!this.middlewares.has(J+U))this.middlewares.set(J+U,[]);if(!this.middlewares.get(J+U).includes(...X))this.middlewares.get(J+U).push(...X)});for(let[X,U]of G){const Y=J+U?.path,Z=U.handler[0],z=U.method[0];this.trie.insert(Y,{handler:Z,method:z})}L.trie=new A}#G(J,L,G){const X=G.slice(0,-1);if(!this.middlewares.has(L))this.middlewares.set(L,[]);if(L==="/")X.forEach((Y)=>{if(!this.globalMiddlewares.includes(Y))this.globalMiddlewares.push(Y)});else if(!this.middlewares.get(L).includes(...X))this.middlewares.get(L).push(...X);const U=G[G.length-1];this.trie.insert(L,{handler:U,method:J})}use(J,L){if(typeof J==="function"){if(!this.globalMiddlewares.includes(J))return this.globalMiddlewares.push(J)}const G=J;if(!this.middlewares.has(G))this.middlewares.set(G,[]);if(!this.middlewares.get(G).includes(L))this.middlewares.get(G).push(L)}get(J,...L){return this.#G("GET",J,L)}post(J,...L){return this.#G("POST",J,L)}put(J,...L){return this.#G("PUT",J,L)}patch(J,...L){if(L.length>0)return this.#G("PATCH",J,L)}delete(J,...L){return this.#G("DELETE",J,L)}}var x=K;export{x as default};
|
package/dist/trie.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class E{constructor(){this.children={},this.isEndOfWord=!1,this.handler=[],this.isDynamic=!1,this.pattern="",this.path="",this.method=[],this.subMiddlewares=new Map}}class F{constructor(){this.root=new E}insert(j,z){let b=this.root;const C=j.split("/").filter(Boolean);if(j==="/"){b.isEndOfWord=!0,b.handler.push(z.handler),b.path=j,b.method.push(z.method);return}for(let q of C){let B=!1,A=q;if(q.startsWith(":"))B=!0,A=":";if(!b.children[A])b.children[A]=new E;b=b.children[A],b.isDynamic=B,b.pattern=q}b.isEndOfWord=!0,b.method.push(z.method),b.handler.push(z.handler),b.path=j}insertMidl(j){if(!this.root.subMiddlewares.has(j))this.root.subMiddlewares.set(j)}search(j,z){let b=this.root;const C=j.split("/").filter(Boolean);for(let B of C){let A=B;if(!b.children[A])if(b.children[":"])b=b.children[":"];else return null;else b=b.children[A]}let q=b.method.indexOf(z);if(q!==-1)return{path:b.path,handler:b.handler[q],isDynamic:b.isDynamic,pattern:b.pattern,method:b.method[q]};return{path:b.path,handler:b.handler,isDynamic:b.isDynamic,pattern:b.pattern,method:b.method[q]}}getAllRoutes(){const j=[],z=(b,C)=>{if(b.isEndOfWord)j.push({path:C,handler:b.handler,isImportant:b.isImportant,isDynamic:b.isDynamic,pattern:b.pattern});for(let q in b.children){const B=b.children[q],A=C+(q===":"?"/:"+B.pattern:"/"+q);z(B,A)}};return z(this.root,""),j}}export{F as default};
|
package/package.json
CHANGED
package/src/ctx.js
CHANGED
|
@@ -5,25 +5,35 @@ export default function createCtx(req, url) {
|
|
|
5
5
|
let parsedQuery = null;
|
|
6
6
|
let parsedCookie = null;
|
|
7
7
|
let parsedParams = null;
|
|
8
|
-
let parsedBody= null
|
|
8
|
+
let parsedBody = null;
|
|
9
|
+
let responseStatus = 200; // Default status
|
|
9
10
|
|
|
10
11
|
return {
|
|
11
12
|
req,
|
|
12
13
|
url,
|
|
13
14
|
next: () => {},
|
|
14
|
-
|
|
15
|
+
|
|
16
|
+
// Set response status for chaining
|
|
17
|
+
status(status) {
|
|
18
|
+
responseStatus = status;
|
|
19
|
+
return this;
|
|
20
|
+
},
|
|
21
|
+
|
|
15
22
|
async body() {
|
|
16
23
|
if (!parsedBody) {
|
|
17
|
-
parsedBody = await parseBody(req)
|
|
24
|
+
parsedBody = await parseBody(req);
|
|
18
25
|
}
|
|
19
26
|
return parsedBody;
|
|
20
27
|
},
|
|
28
|
+
|
|
21
29
|
setHeader(key, value) {
|
|
22
30
|
headers[key] = value;
|
|
31
|
+
return this;
|
|
23
32
|
},
|
|
24
33
|
|
|
25
34
|
set(key, value) {
|
|
26
35
|
settedValue[key] = value;
|
|
36
|
+
return this;
|
|
27
37
|
},
|
|
28
38
|
|
|
29
39
|
get(key) {
|
|
@@ -32,22 +42,30 @@ export default function createCtx(req, url) {
|
|
|
32
42
|
|
|
33
43
|
setAuth(authStatus) {
|
|
34
44
|
isAuthenticated = authStatus;
|
|
45
|
+
return this;
|
|
35
46
|
},
|
|
36
47
|
|
|
37
48
|
getAuth() {
|
|
38
49
|
return isAuthenticated;
|
|
39
50
|
},
|
|
40
51
|
|
|
41
|
-
|
|
52
|
+
// Response methods with optional status
|
|
53
|
+
text(data, status) {
|
|
54
|
+
if (status) {
|
|
55
|
+
responseStatus = status;
|
|
56
|
+
}
|
|
42
57
|
return new Response(data, {
|
|
43
|
-
status,
|
|
58
|
+
status: responseStatus,
|
|
44
59
|
headers: headers,
|
|
45
60
|
});
|
|
46
61
|
},
|
|
47
62
|
|
|
48
|
-
json(data, status
|
|
63
|
+
json(data, status) {
|
|
64
|
+
if (status) {
|
|
65
|
+
responseStatus = status;
|
|
66
|
+
}
|
|
49
67
|
return new Response(JSON.stringify(data), {
|
|
50
|
-
status,
|
|
68
|
+
status: responseStatus,
|
|
51
69
|
headers: {
|
|
52
70
|
"Content-Type": "application/json",
|
|
53
71
|
...headers,
|
|
@@ -55,26 +73,36 @@ export default function createCtx(req, url) {
|
|
|
55
73
|
});
|
|
56
74
|
},
|
|
57
75
|
|
|
58
|
-
html(filepath) {
|
|
76
|
+
html(filepath, status) {
|
|
77
|
+
if (status) {
|
|
78
|
+
responseStatus = status;
|
|
79
|
+
}
|
|
59
80
|
return new Response(Bun.file(filepath), {
|
|
60
|
-
status:
|
|
81
|
+
status: responseStatus,
|
|
61
82
|
headers: {
|
|
62
83
|
...headers,
|
|
63
84
|
},
|
|
64
85
|
});
|
|
65
86
|
},
|
|
66
|
-
|
|
87
|
+
|
|
88
|
+
file(filePath, status) {
|
|
89
|
+
if (status) {
|
|
90
|
+
responseStatus = status;
|
|
91
|
+
}
|
|
67
92
|
return new Response(Bun.file(filePath), {
|
|
68
|
-
status:
|
|
93
|
+
status: responseStatus,
|
|
69
94
|
headers: {
|
|
70
95
|
...headers,
|
|
71
96
|
},
|
|
72
97
|
});
|
|
73
98
|
},
|
|
74
99
|
|
|
75
|
-
redirect(path, status
|
|
100
|
+
redirect(path, status) {
|
|
101
|
+
if (status) {
|
|
102
|
+
responseStatus = status;
|
|
103
|
+
}
|
|
76
104
|
return new Response(null, {
|
|
77
|
-
status,
|
|
105
|
+
status: responseStatus,
|
|
78
106
|
headers: {
|
|
79
107
|
Location: path,
|
|
80
108
|
...headers,
|
|
@@ -82,21 +110,21 @@ export default function createCtx(req, url) {
|
|
|
82
110
|
});
|
|
83
111
|
},
|
|
84
112
|
|
|
85
|
-
|
|
113
|
+
getParams(props) {
|
|
86
114
|
if (!parsedParams) {
|
|
87
|
-
parsedParams =
|
|
115
|
+
parsedParams = extractDynamicParams(req.routePattern, url.pathname);
|
|
88
116
|
}
|
|
89
117
|
return props ? parsedParams[props] : parsedParams;
|
|
90
118
|
},
|
|
91
119
|
|
|
92
120
|
getQuery(props) {
|
|
93
121
|
if (!parsedQuery) {
|
|
94
|
-
parsedQuery = Object.fromEntries(url.searchParams
|
|
122
|
+
parsedQuery = Object.fromEntries(url.searchParams);
|
|
95
123
|
}
|
|
96
124
|
return props ? parsedQuery[props] : parsedQuery;
|
|
97
125
|
},
|
|
98
126
|
|
|
99
|
-
cookie(name, value, options = {}) {
|
|
127
|
+
async cookie(name, value, options = {}) {
|
|
100
128
|
let cookieString = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
|
|
101
129
|
|
|
102
130
|
// Add options to cookie string (e.g., expiration, path, HttpOnly, etc.)
|
|
@@ -109,19 +137,15 @@ export default function createCtx(req, url) {
|
|
|
109
137
|
if (options.sameSite) cookieString += `; SameSite=${options.sameSite}`;
|
|
110
138
|
|
|
111
139
|
if (headers["Set-Cookie"]) {
|
|
112
|
-
// If it's already an array, push the new cookie, otherwise convert to array
|
|
113
140
|
const existingCookies = Array.isArray(headers["Set-Cookie"]) ? headers["Set-Cookie"] : [headers["Set-Cookie"]];
|
|
114
|
-
|
|
115
|
-
// Add the new cookie string to the array
|
|
116
141
|
existingCookies.push(cookieString);
|
|
117
|
-
|
|
118
|
-
// Update Set-Cookie header
|
|
119
142
|
headers["Set-Cookie"] = existingCookies;
|
|
120
143
|
} else {
|
|
121
|
-
// If no cookies exist, initialize the header
|
|
122
144
|
headers["Set-Cookie"] = cookieString;
|
|
123
145
|
}
|
|
146
|
+
return this;
|
|
124
147
|
},
|
|
148
|
+
|
|
125
149
|
async getCookie(cookieName) {
|
|
126
150
|
if (!parsedCookie) {
|
|
127
151
|
parsedCookie = await parseCookie(req.headers.get("cookie"));
|
|
@@ -131,57 +155,66 @@ export default function createCtx(req, url) {
|
|
|
131
155
|
};
|
|
132
156
|
}
|
|
133
157
|
|
|
158
|
+
|
|
134
159
|
async function parseCookie(header) {
|
|
135
160
|
const cookies = {};
|
|
136
161
|
if (!header) return cookies;
|
|
137
162
|
|
|
138
163
|
const cookieArray = header.split(";");
|
|
139
164
|
cookieArray.forEach((cookie) => {
|
|
140
|
-
const [cookieName,
|
|
141
|
-
cookies[cookieName] =
|
|
165
|
+
const [cookieName, cookieValue] = cookie.trim().split("=");
|
|
166
|
+
cookies[cookieName] = cookieValue.split(" ")[0];
|
|
142
167
|
});
|
|
143
168
|
return cookies;
|
|
144
169
|
}
|
|
145
170
|
|
|
146
|
-
|
|
171
|
+
function extractDynamicParams(routePattern, path) {
|
|
147
172
|
const object = {};
|
|
148
173
|
const routeSegments = routePattern.split("/");
|
|
149
|
-
const [pathWithoutQuery] = path.split("?");
|
|
150
|
-
const pathSegments = pathWithoutQuery.split("/");
|
|
174
|
+
const [pathWithoutQuery] = path.split("?");
|
|
175
|
+
const pathSegments = pathWithoutQuery.split("/");
|
|
151
176
|
|
|
152
177
|
if (routeSegments.length !== pathSegments.length) {
|
|
153
|
-
return null;
|
|
178
|
+
return null;
|
|
154
179
|
}
|
|
155
180
|
|
|
156
181
|
routeSegments.forEach((segment, index) => {
|
|
157
182
|
if (segment.startsWith(":")) {
|
|
158
|
-
const dynamicKey = segment.slice(1);
|
|
159
|
-
object[dynamicKey] = pathSegments[index];
|
|
183
|
+
const dynamicKey = segment.slice(1);
|
|
184
|
+
object[dynamicKey] = pathSegments[index];
|
|
160
185
|
}
|
|
161
186
|
});
|
|
162
187
|
|
|
163
188
|
return object;
|
|
164
|
-
}
|
|
189
|
+
}
|
|
165
190
|
|
|
166
191
|
async function parseBody(req) {
|
|
167
|
-
const contentType = req.headers.get("Content-Type");
|
|
168
|
-
|
|
169
|
-
|
|
192
|
+
const contentType = req.headers.get("Content-Type") || "";
|
|
193
|
+
|
|
194
|
+
if (!contentType) return {};
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
if (contentType.startsWith("application/json")) {
|
|
170
198
|
return await req.json();
|
|
171
|
-
} catch (error) {
|
|
172
|
-
return new Response({ error: "Invalid JSON format" });
|
|
173
199
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
200
|
+
|
|
201
|
+
if (contentType.startsWith("application/x-www-form-urlencoded")) {
|
|
202
|
+
const body = await req.text();
|
|
203
|
+
return Object.fromEntries(new URLSearchParams(body));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (contentType.startsWith("multipart/form-data")) {
|
|
207
|
+
const formData = await req.formData();
|
|
208
|
+
return formDataToObject(formData);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return new Response({ error: "Unknown request body type" });
|
|
212
|
+
} catch (error) {
|
|
213
|
+
return new Response({ error: "Invalid request body format" });
|
|
182
214
|
}
|
|
183
215
|
}
|
|
184
216
|
|
|
217
|
+
|
|
185
218
|
function formDataToObject(formData) {
|
|
186
219
|
const obj = {};
|
|
187
220
|
for (const [key, value] of formData.entries()) {
|
package/src/handleRequest.js
CHANGED
|
@@ -1,46 +1,68 @@
|
|
|
1
1
|
import createCtx from "./ctx";
|
|
2
2
|
|
|
3
3
|
export default async function handleRequest(req, url, diesel) {
|
|
4
|
-
|
|
5
|
-
const
|
|
4
|
+
|
|
5
|
+
const ctx = createCtx(req, url);
|
|
6
|
+
|
|
7
|
+
// OnReq hook 1
|
|
8
|
+
if (diesel.hasOnReqHook) {
|
|
9
|
+
await diesel.hooks.onRequest(ctx)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// middleware execution
|
|
13
|
+
if (diesel.hasMiddleware) {
|
|
14
|
+
const middlewares = [
|
|
15
|
+
...diesel.globalMiddlewares,
|
|
16
|
+
...diesel.middlewares.get(url.pathname) || []
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const middlewareResult = await executeMiddleware(middlewares, ctx);
|
|
20
|
+
if (middlewareResult) return middlewareResult;
|
|
21
|
+
}
|
|
6
22
|
|
|
7
23
|
// Try to find the route handler in the trie
|
|
8
|
-
const routeHandler = diesel.trie.search(pathname, method);
|
|
9
|
-
|
|
24
|
+
const routeHandler = diesel.trie.search(url.pathname, req.method);
|
|
25
|
+
|
|
10
26
|
// Early return if route or method is not found
|
|
11
|
-
if (!routeHandler || !routeHandler.handler) return responseNotFound(pathname);
|
|
12
|
-
if (routeHandler.method !== method) return responseMethodNotAllowed();
|
|
27
|
+
if (!routeHandler || !routeHandler.handler) return responseNotFound(url.pathname);
|
|
28
|
+
if (routeHandler.method !== req.method) return responseMethodNotAllowed();
|
|
29
|
+
|
|
13
30
|
// If the route is dynamic, we only set routePattern if necessary
|
|
14
31
|
if (routeHandler.isDynamic) req.routePattern = routeHandler.path;
|
|
15
32
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
...diesel.middlewares.get(pathname) || []
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
const middlewareResult = await executeMiddleware(middlewares, ctx);
|
|
25
|
-
if (middlewareResult) return middlewareResult;
|
|
33
|
+
// Run preHandler hooks 2
|
|
34
|
+
if (diesel.hasPreHandlerHook) {
|
|
35
|
+
const Hookresult = await diesel.hooks.preHandler(ctx);
|
|
36
|
+
if(Hookresult) return Hookresult;
|
|
37
|
+
}
|
|
26
38
|
|
|
27
|
-
}
|
|
28
|
-
|
|
29
39
|
|
|
30
40
|
// Finally, execute the route handler and return its result
|
|
31
41
|
try {
|
|
32
42
|
const result = await routeHandler.handler(ctx);
|
|
43
|
+
|
|
44
|
+
// 3. run the postHandler hooks
|
|
45
|
+
if (diesel.hasPostHandlerHook) {
|
|
46
|
+
await diesel.hooks.postHandler(ctx)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 4. Run onSend hooks before sending the response
|
|
50
|
+
if (diesel.hasOnSendHook) {
|
|
51
|
+
const hookResponse = await diesel.hooks.onSend(result, ctx);
|
|
52
|
+
if(hookResponse) return hookResponse
|
|
53
|
+
}
|
|
54
|
+
|
|
33
55
|
return result ?? responseNoHandler();
|
|
34
56
|
} catch (error) {
|
|
35
57
|
return responseServerError();
|
|
36
58
|
}
|
|
37
59
|
}
|
|
38
60
|
|
|
39
|
-
//
|
|
61
|
+
// middleware execution
|
|
40
62
|
async function executeMiddleware(middlewares, ctx) {
|
|
41
63
|
for (const middleware of middlewares) {
|
|
42
64
|
const result = await middleware(ctx);
|
|
43
|
-
if (result) return result;
|
|
65
|
+
if (result) return result;
|
|
44
66
|
}
|
|
45
67
|
}
|
|
46
68
|
|
package/src/server.js
CHANGED
|
@@ -1,136 +1,194 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import Trie from "./trie.js";
|
|
2
|
+
import handleRequest from "./handleRequest.js";
|
|
3
|
+
|
|
4
|
+
class Diesel {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.routes = new Map();
|
|
7
|
+
this.globalMiddlewares = [];
|
|
8
|
+
this.middlewares = new Map();
|
|
9
|
+
this.trie = new Trie();
|
|
10
|
+
this.hasMiddleware = false;
|
|
11
|
+
this.hasOnReqHook=false;
|
|
12
|
+
this.hasPreHandlerHook=false;
|
|
13
|
+
this.hasPostHandlerHook=false;
|
|
14
|
+
this.hasOnSendHook=false;
|
|
15
|
+
this.hooks = {
|
|
16
|
+
onRequest: null,
|
|
17
|
+
preHandler: null,
|
|
18
|
+
postHandler: null,
|
|
19
|
+
onSend: null,
|
|
20
|
+
onError: null,
|
|
21
|
+
onClose: null
|
|
12
22
|
}
|
|
23
|
+
}
|
|
13
24
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (middlewares.length > 0) {
|
|
20
|
-
this.hasMiddleware = true;
|
|
21
|
-
break;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
25
|
+
addHooks(typeOfHook,fnc){
|
|
26
|
+
if (this.hooks.hasOwnProperty(typeOfHook)) {
|
|
27
|
+
this.hooks[typeOfHook] = fnc; // Overwrite or set the hook
|
|
28
|
+
} else {
|
|
29
|
+
throw new Error(`Unknown hook type: ${typeOfHook}`); // Throw an error for invalid hook types
|
|
24
30
|
}
|
|
31
|
+
}
|
|
25
32
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
port,
|
|
30
|
-
fetch: async (req) => {
|
|
31
|
-
const url = new URL(req.url)
|
|
32
|
-
try {
|
|
33
|
-
return await handleRequest(req,url,this)
|
|
34
|
-
} catch (error) {
|
|
35
|
-
return new Response('Internal Server Error', { status: 500 });
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
onClose() {
|
|
39
|
-
console.log("Server is shutting down...");
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
if (typeof callback === 'function') {
|
|
43
|
-
return callback();
|
|
44
|
-
}
|
|
45
|
-
console.log(`Server is running on http://localhost:${port}`);
|
|
46
|
-
return server;
|
|
33
|
+
compile() {
|
|
34
|
+
if (this.globalMiddlewares.length > 0) {
|
|
35
|
+
this.hasMiddleware = true;
|
|
47
36
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
37
|
+
for (const [path, middlewares] of this.middlewares.entries()) {
|
|
38
|
+
if (middlewares.length > 0) {
|
|
39
|
+
this.hasMiddleware = true;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// check if hook is present or not
|
|
45
|
+
if (this.hooks.onRequest) this.hasOnReqHook=true;
|
|
46
|
+
if(this.hooks.preHandler) this.hasPreHandlerHook=true;
|
|
47
|
+
if(this.hooks.postHandler) this.hasPostHandlerHook=true;
|
|
48
|
+
if(this.hooks.onSend) this.hasOnSendHook=true;
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
listen(port, { sslCert = null, sslKey = null } = {}, callback) {
|
|
53
|
+
if (typeof Bun === 'undefined')
|
|
54
|
+
throw new Error(
|
|
55
|
+
'.listen() is designed to run on Bun only...'
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if (typeof port !== "number") {
|
|
59
|
+
throw new Error('Port must be a numeric value')
|
|
67
60
|
}
|
|
68
61
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const middlewareHandlers = handlers.slice(0, -1);
|
|
62
|
+
this.compile();
|
|
72
63
|
|
|
73
|
-
|
|
74
|
-
|
|
64
|
+
const options = {
|
|
65
|
+
port,
|
|
66
|
+
fetch: async (req) => {
|
|
67
|
+
const url = new URL(req.url);
|
|
68
|
+
try {
|
|
69
|
+
return await handleRequest(req, url, this);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
return new Response("Internal Server Error", { status: 500 });
|
|
75
72
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
73
|
+
},
|
|
74
|
+
onClose() {
|
|
75
|
+
console.log("Server is shutting down...");
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
if (sslCert && sslKey) {
|
|
80
|
+
options.certFile = sslCert;
|
|
81
|
+
options.keyFile = sslKey;
|
|
82
|
+
}
|
|
83
|
+
const server = Bun.serve(options);
|
|
84
|
+
|
|
85
|
+
Bun?.gc(false)
|
|
86
|
+
|
|
87
|
+
if (typeof callback === "function") {
|
|
88
|
+
return callback();
|
|
91
89
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return this.globalMiddlewares.push(pathORHandler)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// now it means it is path midl
|
|
101
|
-
const path = pathORHandler
|
|
102
|
-
if (!this.middlewares.has(path)) {
|
|
103
|
-
this.middlewares.set(path,[])
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if(!this.middlewares.get(path).includes(handler)){
|
|
107
|
-
this.middlewares.get(path).push(handler)
|
|
108
|
-
}
|
|
90
|
+
|
|
91
|
+
if (sslCert && sslKey) {
|
|
92
|
+
console.log(`HTTPS server is running on https://localhost:${port}`);
|
|
93
|
+
} else {
|
|
94
|
+
console.log(`HTTP server is running on http://localhost:${port}`);
|
|
109
95
|
}
|
|
110
96
|
|
|
111
|
-
|
|
112
|
-
|
|
97
|
+
return server;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
register(pathPrefix, handlerInstance) {
|
|
101
|
+
const routeEntries = Object.entries(handlerInstance.trie.root.children);
|
|
102
|
+
// console.log(handlerInstance.trie.root);
|
|
103
|
+
handlerInstance.trie.root.subMiddlewares.forEach((middleware, path) => {
|
|
104
|
+
if (!this.middlewares.has(pathPrefix + path)) {
|
|
105
|
+
this.middlewares.set(pathPrefix + path, []);
|
|
106
|
+
}
|
|
107
|
+
if (!this.middlewares.get(pathPrefix + path).includes(...middleware)) {
|
|
108
|
+
this.middlewares.get(pathPrefix + path).push(...middleware);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
for (const [routeKey, routeNode] of routeEntries) {
|
|
112
|
+
const fullpath = pathPrefix + routeNode?.path;
|
|
113
|
+
const routeHandler = routeNode.handler[0];
|
|
114
|
+
const httpMethod = routeNode.method[0];
|
|
115
|
+
this.trie.insert(fullpath, { handler: routeHandler, method: httpMethod });
|
|
113
116
|
}
|
|
117
|
+
handlerInstance.trie = new Trie();
|
|
118
|
+
}
|
|
114
119
|
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
#addRoute(method, path, handlers) {
|
|
121
|
+
if(typeof path !== "string"){
|
|
122
|
+
throw new Error("Path must be a string type");
|
|
117
123
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return this.#addRoute("PUT",path,handlers)
|
|
124
|
+
if(typeof method !== "string"){
|
|
125
|
+
throw new Error("method must be a string type");
|
|
121
126
|
}
|
|
127
|
+
const middlewareHandlers = handlers.slice(0, -1);
|
|
122
128
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
if (!this.middlewares.has(path)) {
|
|
130
|
+
this.middlewares.set(path, []);
|
|
131
|
+
}
|
|
132
|
+
if (path === "/") {
|
|
133
|
+
middlewareHandlers.forEach((midlleware) => {
|
|
134
|
+
if (!this.globalMiddlewares.includes(midlleware)) {
|
|
135
|
+
this.globalMiddlewares.push(midlleware);
|
|
126
136
|
}
|
|
137
|
+
});
|
|
138
|
+
} else {
|
|
139
|
+
if (!this.middlewares.get(path).includes(...middlewareHandlers)) {
|
|
140
|
+
this.middlewares.get(path).push(...middlewareHandlers);
|
|
141
|
+
}
|
|
127
142
|
}
|
|
128
143
|
|
|
129
|
-
|
|
130
|
-
|
|
144
|
+
const handler = handlers[handlers.length - 1];
|
|
145
|
+
this.trie.insert(path, { handler, method });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
use(pathORHandler, handler) {
|
|
149
|
+
if (typeof pathORHandler === "function") {
|
|
150
|
+
if (!this.globalMiddlewares.includes(pathORHandler)) {
|
|
151
|
+
return this.globalMiddlewares.push(pathORHandler);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// now it means it is path midl
|
|
155
|
+
const path = pathORHandler;
|
|
156
|
+
if (!this.middlewares.has(path)) {
|
|
157
|
+
this.middlewares.set(path, []);
|
|
131
158
|
}
|
|
132
159
|
|
|
160
|
+
if (!this.middlewares.get(path).includes(handler)) {
|
|
161
|
+
this.middlewares.get(path).push(handler);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
get(path, ...handlers) {
|
|
166
|
+
this.#addRoute("GET", path, handlers);
|
|
167
|
+
return this
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
post(path, ...handlers) {
|
|
171
|
+
this.#addRoute("POST", path, handlers);
|
|
172
|
+
return this
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
put(path, ...handlers) {
|
|
176
|
+
this.#addRoute("PUT", path, handlers);
|
|
177
|
+
return this
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
patch(path, ...handlers) {
|
|
181
|
+
if(typeof path !== "string"){
|
|
182
|
+
throw new Error("Path must be a string type");
|
|
183
|
+
}
|
|
184
|
+
this.#addRoute("PATCH", path, handlers);
|
|
185
|
+
return this
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
delete(path, ...handlers) {
|
|
189
|
+
this.#addRoute("DELETE", path, handlers);
|
|
190
|
+
return this;
|
|
191
|
+
}
|
|
133
192
|
}
|
|
134
193
|
|
|
135
|
-
|
|
136
|
-
export default diesel;
|
|
194
|
+
export default Diesel
|
package/src/trie.js
CHANGED
|
@@ -3,7 +3,6 @@ class TrieNode {
|
|
|
3
3
|
this.children = {};
|
|
4
4
|
this.isEndOfWord = false;
|
|
5
5
|
this.handler = [];
|
|
6
|
-
this.isImportant = false;
|
|
7
6
|
this.isDynamic = false;
|
|
8
7
|
this.pattern = '';
|
|
9
8
|
this.path = "";
|
|
@@ -25,7 +24,6 @@ class TrieNode {
|
|
|
25
24
|
if (path === '/') {
|
|
26
25
|
node.isEndOfWord = true;
|
|
27
26
|
node.handler.push(route.handler)
|
|
28
|
-
node.isImportant = route.isImportant;
|
|
29
27
|
node.path = path;
|
|
30
28
|
node.method.push(route.method)
|
|
31
29
|
return;
|
|
@@ -54,7 +52,6 @@ class TrieNode {
|
|
|
54
52
|
node.isEndOfWord = true;
|
|
55
53
|
node.method.push(route.method);
|
|
56
54
|
node.handler.push(route.handler)
|
|
57
|
-
node.isImportant = route.isImportant;
|
|
58
55
|
node.path = path; // Store the original path
|
|
59
56
|
}
|
|
60
57
|
|