@stacksjs/bun-router 0.0.8 → 0.0.9
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/{chunk-jhsk5c5b.js → chunk-n5pvpz7q.js} +1430 -2840
- package/dist/cli.js +2 -2
- package/dist/index.js +591 -589
- package/dist/response/response-factory.d.ts +1 -1
- package/package.json +1 -1
- package/src/development/typescript-utilities.ts +4 -0
- package/src/response/response-factory.ts +13 -5
- package/src/router/server.ts +8 -1
package/dist/index.js
CHANGED
|
@@ -66,7 +66,7 @@ import {
|
|
|
66
66
|
signRequest,
|
|
67
67
|
singleFileUpload,
|
|
68
68
|
verifySignature
|
|
69
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-n5pvpz7q.js";
|
|
70
70
|
|
|
71
71
|
// src/auth.ts
|
|
72
72
|
function base64UrlEncode(str) {
|
|
@@ -2787,7 +2787,8 @@ function request() {
|
|
|
2787
2787
|
// src/response/response-factory.ts
|
|
2788
2788
|
var response = {
|
|
2789
2789
|
json: (data, options = {}) => {
|
|
2790
|
-
const
|
|
2790
|
+
const opts = typeof options === "number" ? { status: options } : options;
|
|
2791
|
+
const { status = 200, headers = {}, pretty = false } = opts;
|
|
2791
2792
|
const body = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
|
|
2792
2793
|
return new Response(body, {
|
|
2793
2794
|
status,
|
|
@@ -9546,513 +9547,6 @@ function applyRequestEnhancements(req, params = {}) {
|
|
|
9546
9547
|
return getEnhancementHost().enhanceRequest(req, params);
|
|
9547
9548
|
}
|
|
9548
9549
|
|
|
9549
|
-
// src/router/server.ts
|
|
9550
|
-
function registerServerHandling(RouterClass) {
|
|
9551
|
-
Object.defineProperties(RouterClass.prototype, {
|
|
9552
|
-
serve: {
|
|
9553
|
-
async value(options = {}) {
|
|
9554
|
-
if (this._initFileRoutes) {
|
|
9555
|
-
await this._initFileRoutes();
|
|
9556
|
-
}
|
|
9557
|
-
if (this._initApiRoutes) {
|
|
9558
|
-
await this._initApiRoutes();
|
|
9559
|
-
}
|
|
9560
|
-
this.invalidateCache();
|
|
9561
|
-
const serverOptions = {
|
|
9562
|
-
idleTimeout: 255,
|
|
9563
|
-
...options,
|
|
9564
|
-
fetch: this.handleRequest.bind(this)
|
|
9565
|
-
};
|
|
9566
|
-
const staticRouteMap = {};
|
|
9567
|
-
if (this.staticResponses && this.staticResponses.size > 0) {
|
|
9568
|
-
for (const [path, response2] of this.staticResponses) {
|
|
9569
|
-
staticRouteMap[path] = response2;
|
|
9570
|
-
}
|
|
9571
|
-
}
|
|
9572
|
-
if (options.static) {
|
|
9573
|
-
Object.assign(staticRouteMap, options.static);
|
|
9574
|
-
}
|
|
9575
|
-
if (Object.keys(staticRouteMap).length > 0) {
|
|
9576
|
-
serverOptions.static = staticRouteMap;
|
|
9577
|
-
}
|
|
9578
|
-
if (options.development !== undefined) {
|
|
9579
|
-
serverOptions.development = options.development;
|
|
9580
|
-
}
|
|
9581
|
-
if (this.wsConfig) {
|
|
9582
|
-
serverOptions.websocket = this.wsConfig;
|
|
9583
|
-
}
|
|
9584
|
-
this.serverInstance = Bun.serve(serverOptions);
|
|
9585
|
-
if (this.config.verbose) {
|
|
9586
|
-
const port = this.serverInstance.port;
|
|
9587
|
-
const hostname = this.serverInstance.hostname;
|
|
9588
|
-
console.log(`
|
|
9589
|
-
\uD83D\uDE80 Server running at http://${hostname}:${port}
|
|
9590
|
-
`);
|
|
9591
|
-
}
|
|
9592
|
-
return this.serverInstance;
|
|
9593
|
-
},
|
|
9594
|
-
writable: true,
|
|
9595
|
-
configurable: true
|
|
9596
|
-
},
|
|
9597
|
-
reload: {
|
|
9598
|
-
async value() {
|
|
9599
|
-
if (!this.serverInstance) {
|
|
9600
|
-
throw new Error("Server not started, cannot reload");
|
|
9601
|
-
}
|
|
9602
|
-
this.invalidateCache();
|
|
9603
|
-
const port = this.serverInstance.port;
|
|
9604
|
-
const hostname = this.serverInstance.hostname;
|
|
9605
|
-
this.serverInstance.stop();
|
|
9606
|
-
this.serverInstance = Bun.serve({
|
|
9607
|
-
port,
|
|
9608
|
-
hostname,
|
|
9609
|
-
fetch: this.handleRequest.bind(this),
|
|
9610
|
-
websocket: this.wsConfig || undefined
|
|
9611
|
-
});
|
|
9612
|
-
if (this.config.verbose) {
|
|
9613
|
-
console.log(`\uD83D\uDD04 Server reloaded at http://${hostname}:${port}`);
|
|
9614
|
-
}
|
|
9615
|
-
},
|
|
9616
|
-
writable: true,
|
|
9617
|
-
configurable: true
|
|
9618
|
-
},
|
|
9619
|
-
handleRequest: {
|
|
9620
|
-
async value(req) {
|
|
9621
|
-
const self = this;
|
|
9622
|
-
return runWithRequest(req, () => self.handleRequestImpl(req));
|
|
9623
|
-
},
|
|
9624
|
-
writable: true,
|
|
9625
|
-
configurable: true
|
|
9626
|
-
},
|
|
9627
|
-
handleRequestImpl: {
|
|
9628
|
-
async value(req) {
|
|
9629
|
-
try {
|
|
9630
|
-
const url2 = new URL(req.url);
|
|
9631
|
-
if (req.method === "OPTIONS") {
|
|
9632
|
-
const hostname2 = url2.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
|
|
9633
|
-
const optionsMatch = this.matchRoute(url2.pathname, "OPTIONS", hostname2);
|
|
9634
|
-
if (!optionsMatch) {
|
|
9635
|
-
return new Response(null, {
|
|
9636
|
-
status: 204,
|
|
9637
|
-
headers: {
|
|
9638
|
-
"Access-Control-Allow-Origin": "*",
|
|
9639
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
9640
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With, Accept, Origin",
|
|
9641
|
-
"Access-Control-Max-Age": "86400",
|
|
9642
|
-
"Access-Control-Allow-Credentials": "true"
|
|
9643
|
-
}
|
|
9644
|
-
});
|
|
9645
|
-
}
|
|
9646
|
-
}
|
|
9647
|
-
const hostname = url2.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
|
|
9648
|
-
const match = this.matchRoute(url2.pathname, req.method, hostname);
|
|
9649
|
-
const enhancedReq = this.enhanceRequest(req, match?.params || {});
|
|
9650
|
-
setCurrentRequest(enhancedReq);
|
|
9651
|
-
if (match) {
|
|
9652
|
-
enhancedReq.route = match.route;
|
|
9653
|
-
const middlewareStack = [...this.globalMiddleware];
|
|
9654
|
-
if (match.route.middleware && match.route.middleware.length > 0) {
|
|
9655
|
-
middlewareStack.push(...match.route.middleware);
|
|
9656
|
-
}
|
|
9657
|
-
const routeHandlerMiddleware = async (req2, _next) => {
|
|
9658
|
-
return await this.resolveHandler(match.route.handler, req2);
|
|
9659
|
-
};
|
|
9660
|
-
middlewareStack.push(routeHandlerMiddleware);
|
|
9661
|
-
const response2 = await this.runMiddleware(enhancedReq, middlewareStack);
|
|
9662
|
-
if (response2) {
|
|
9663
|
-
return this.applyModifiedCookies(response2, enhancedReq);
|
|
9664
|
-
}
|
|
9665
|
-
return new Response("No response from middleware chain", { status: 500 });
|
|
9666
|
-
}
|
|
9667
|
-
const allowedMethods = this.getAllowedMethods(url2.pathname, hostname);
|
|
9668
|
-
const corsHeaders = {
|
|
9669
|
-
"Content-Type": "application/json",
|
|
9670
|
-
"Access-Control-Allow-Origin": "*",
|
|
9671
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
9672
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With, Accept, Origin"
|
|
9673
|
-
};
|
|
9674
|
-
if (allowedMethods.length > 0) {
|
|
9675
|
-
const methodNotAllowedHandler = async (_req, _next) => {
|
|
9676
|
-
return new Response(JSON.stringify({
|
|
9677
|
-
success: false,
|
|
9678
|
-
message: "Method Not Allowed",
|
|
9679
|
-
path: url2.pathname,
|
|
9680
|
-
method: req.method,
|
|
9681
|
-
allowed: allowedMethods
|
|
9682
|
-
}), {
|
|
9683
|
-
status: 405,
|
|
9684
|
-
headers: { ...corsHeaders, Allow: allowedMethods.join(", ") }
|
|
9685
|
-
});
|
|
9686
|
-
};
|
|
9687
|
-
if (this.globalMiddleware.length > 0) {
|
|
9688
|
-
const stack = [...this.globalMiddleware, methodNotAllowedHandler];
|
|
9689
|
-
const response2 = await this.runMiddleware(enhancedReq, stack);
|
|
9690
|
-
if (response2)
|
|
9691
|
-
return this.applyModifiedCookies(response2, enhancedReq);
|
|
9692
|
-
}
|
|
9693
|
-
return await methodNotAllowedHandler(enhancedReq, async () => null);
|
|
9694
|
-
}
|
|
9695
|
-
if (this.fallbackHandler) {
|
|
9696
|
-
const response2 = await this.resolveHandler(this.fallbackHandler, enhancedReq);
|
|
9697
|
-
return this.applyModifiedCookies(response2, enhancedReq);
|
|
9698
|
-
}
|
|
9699
|
-
const notFoundHandler = async (_req, _next) => {
|
|
9700
|
-
return new Response(JSON.stringify({
|
|
9701
|
-
success: false,
|
|
9702
|
-
message: "Not Found",
|
|
9703
|
-
path: url2.pathname,
|
|
9704
|
-
method: req.method
|
|
9705
|
-
}), { status: 404, headers: corsHeaders });
|
|
9706
|
-
};
|
|
9707
|
-
if (this.globalMiddleware.length > 0) {
|
|
9708
|
-
const stack = [...this.globalMiddleware, notFoundHandler];
|
|
9709
|
-
const response2 = await this.runMiddleware(enhancedReq, stack);
|
|
9710
|
-
if (response2)
|
|
9711
|
-
return this.applyModifiedCookies(response2, enhancedReq);
|
|
9712
|
-
}
|
|
9713
|
-
return await notFoundHandler(enhancedReq, async () => null);
|
|
9714
|
-
} catch (error) {
|
|
9715
|
-
console.error("Error handling request:", error);
|
|
9716
|
-
if (this.errorHandler) {
|
|
9717
|
-
return this.errorHandler(error);
|
|
9718
|
-
}
|
|
9719
|
-
return new Response(JSON.stringify({
|
|
9720
|
-
success: false,
|
|
9721
|
-
message: "Internal Server Error",
|
|
9722
|
-
error: error instanceof Error ? error.message : String(error)
|
|
9723
|
-
}), {
|
|
9724
|
-
status: 500,
|
|
9725
|
-
headers: {
|
|
9726
|
-
"Content-Type": "application/json",
|
|
9727
|
-
"Access-Control-Allow-Origin": "*",
|
|
9728
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
9729
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With, Accept, Origin"
|
|
9730
|
-
}
|
|
9731
|
-
});
|
|
9732
|
-
}
|
|
9733
|
-
},
|
|
9734
|
-
writable: true,
|
|
9735
|
-
configurable: true
|
|
9736
|
-
},
|
|
9737
|
-
enhanceRequest: {
|
|
9738
|
-
value(req, params = {}) {
|
|
9739
|
-
let parsedCookies = null;
|
|
9740
|
-
const getCookies = () => {
|
|
9741
|
-
if (parsedCookies === null) {
|
|
9742
|
-
parsedCookies = {};
|
|
9743
|
-
const cookieHeader = req.headers.get("cookie") || "";
|
|
9744
|
-
cookieHeader.split(";").forEach((cookie) => {
|
|
9745
|
-
const parts = cookie.trim().split("=");
|
|
9746
|
-
if (parts.length >= 2) {
|
|
9747
|
-
const name = parts[0].trim();
|
|
9748
|
-
const value = parts.slice(1).join("=").trim();
|
|
9749
|
-
parsedCookies[name] = decodeURIComponent(value);
|
|
9750
|
-
}
|
|
9751
|
-
});
|
|
9752
|
-
}
|
|
9753
|
-
return parsedCookies;
|
|
9754
|
-
};
|
|
9755
|
-
const cookies = {
|
|
9756
|
-
get: (name) => getCookies()[name],
|
|
9757
|
-
set: (name, value, options = {}) => {
|
|
9758
|
-
const enhancedRequest = req;
|
|
9759
|
-
if (!enhancedRequest._cookiesToSet) {
|
|
9760
|
-
enhancedRequest._cookiesToSet = [];
|
|
9761
|
-
}
|
|
9762
|
-
enhancedRequest._cookiesToSet.push({ name, value, options });
|
|
9763
|
-
},
|
|
9764
|
-
delete: (name, options = {}) => {
|
|
9765
|
-
const enhancedRequest = req;
|
|
9766
|
-
if (!enhancedRequest._cookiesToDelete) {
|
|
9767
|
-
enhancedRequest._cookiesToDelete = [];
|
|
9768
|
-
}
|
|
9769
|
-
enhancedRequest._cookiesToDelete.push({ name, options });
|
|
9770
|
-
},
|
|
9771
|
-
getAll: () => ({ ...getCookies() })
|
|
9772
|
-
};
|
|
9773
|
-
const enhancedReq = Object.assign(req, {
|
|
9774
|
-
params,
|
|
9775
|
-
cookies: getCookies(),
|
|
9776
|
-
_cookiesToSet: [],
|
|
9777
|
-
_cookiesToDelete: []
|
|
9778
|
-
});
|
|
9779
|
-
Object.assign(enhancedReq, { cookies: { ...getCookies(), ...cookies } });
|
|
9780
|
-
return enhancedReq;
|
|
9781
|
-
},
|
|
9782
|
-
writable: true,
|
|
9783
|
-
configurable: true
|
|
9784
|
-
},
|
|
9785
|
-
applyModifiedCookies: {
|
|
9786
|
-
value(response2, req) {
|
|
9787
|
-
const newResponse = new Response(response2.body, {
|
|
9788
|
-
status: response2.status,
|
|
9789
|
-
statusText: response2.statusText,
|
|
9790
|
-
headers: response2.headers
|
|
9791
|
-
});
|
|
9792
|
-
if (req._cookiesToSet && req._cookiesToSet.length > 0) {
|
|
9793
|
-
for (const { name, value, options } of req._cookiesToSet) {
|
|
9794
|
-
const cookieString = this.serializeCookie(name, value, options);
|
|
9795
|
-
newResponse.headers.append("Set-Cookie", cookieString);
|
|
9796
|
-
}
|
|
9797
|
-
}
|
|
9798
|
-
if (req._cookiesToDelete && req._cookiesToDelete.length > 0) {
|
|
9799
|
-
for (const { name, options } of req._cookiesToDelete) {
|
|
9800
|
-
const deletionOptions = {
|
|
9801
|
-
...options,
|
|
9802
|
-
expires: new Date(0),
|
|
9803
|
-
maxAge: 0
|
|
9804
|
-
};
|
|
9805
|
-
const cookieString = this.serializeCookie(name, "", deletionOptions);
|
|
9806
|
-
newResponse.headers.append("Set-Cookie", cookieString);
|
|
9807
|
-
}
|
|
9808
|
-
}
|
|
9809
|
-
return newResponse;
|
|
9810
|
-
},
|
|
9811
|
-
writable: true,
|
|
9812
|
-
configurable: true
|
|
9813
|
-
},
|
|
9814
|
-
serializeCookie: {
|
|
9815
|
-
value(name, value, options = {}) {
|
|
9816
|
-
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
|
|
9817
|
-
if (options.maxAge !== undefined) {
|
|
9818
|
-
cookie += `; Max-Age=${options.maxAge}`;
|
|
9819
|
-
}
|
|
9820
|
-
if (options.expires && options.expires instanceof Date) {
|
|
9821
|
-
cookie += `; Expires=${options.expires.toUTCString()}`;
|
|
9822
|
-
}
|
|
9823
|
-
if (options.path) {
|
|
9824
|
-
cookie += `; Path=${options.path}`;
|
|
9825
|
-
} else {
|
|
9826
|
-
cookie += "; Path=/";
|
|
9827
|
-
}
|
|
9828
|
-
if (options.domain) {
|
|
9829
|
-
cookie += `; Domain=${options.domain}`;
|
|
9830
|
-
}
|
|
9831
|
-
if (options.secure) {
|
|
9832
|
-
cookie += "; Secure";
|
|
9833
|
-
}
|
|
9834
|
-
if (options.httpOnly) {
|
|
9835
|
-
cookie += "; HttpOnly";
|
|
9836
|
-
}
|
|
9837
|
-
if (options.sameSite) {
|
|
9838
|
-
const sameSite = options.sameSite.toLowerCase();
|
|
9839
|
-
cookie += `; SameSite=${sameSite.charAt(0).toUpperCase() + sameSite.slice(1)}`;
|
|
9840
|
-
}
|
|
9841
|
-
return cookie;
|
|
9842
|
-
},
|
|
9843
|
-
writable: true,
|
|
9844
|
-
configurable: true
|
|
9845
|
-
}
|
|
9846
|
-
});
|
|
9847
|
-
}
|
|
9848
|
-
|
|
9849
|
-
// src/router/view-rendering.ts
|
|
9850
|
-
import { join as join4 } from "path";
|
|
9851
|
-
function registerViewRendering(RouterClass) {
|
|
9852
|
-
Object.defineProperties(RouterClass.prototype, {
|
|
9853
|
-
renderView: {
|
|
9854
|
-
async value(view, data = {}, options = {}) {
|
|
9855
|
-
const viewsConfig = this.config.views || {
|
|
9856
|
-
viewsPath: "resources/views",
|
|
9857
|
-
extensions: [".html", ".stx"],
|
|
9858
|
-
cache: Bun.env.NODE_ENV === "production",
|
|
9859
|
-
engine: "auto"
|
|
9860
|
-
};
|
|
9861
|
-
const cacheKey = `${view}:${options.layout || ""}`;
|
|
9862
|
-
if (viewsConfig.cache && this.templateCache.has(cacheKey)) {
|
|
9863
|
-
return this.processTemplate(this.templateCache.get(cacheKey), data, viewsConfig);
|
|
9864
|
-
}
|
|
9865
|
-
const viewPath = await resolveViewPath(view, viewsConfig.viewsPath, viewsConfig.extensions);
|
|
9866
|
-
if (!viewPath) {
|
|
9867
|
-
throw new Error(`View '${view}' not found`);
|
|
9868
|
-
}
|
|
9869
|
-
let viewContent = await Bun.file(viewPath).text();
|
|
9870
|
-
const layoutName = options.layout || viewsConfig.defaultLayout;
|
|
9871
|
-
if (layoutName) {
|
|
9872
|
-
const layoutPath = await resolveViewPath(join4("layouts", layoutName), viewsConfig.viewsPath, viewsConfig.extensions);
|
|
9873
|
-
if (!layoutPath) {
|
|
9874
|
-
throw new Error(`Layout '${layoutName}' not found`);
|
|
9875
|
-
}
|
|
9876
|
-
const layoutContent = await Bun.file(layoutPath).text();
|
|
9877
|
-
viewContent = layoutContent.replace(/\{\{\s*content\s*\}\}/g, viewContent);
|
|
9878
|
-
}
|
|
9879
|
-
if (viewsConfig.cache) {
|
|
9880
|
-
this.templateCache.set(cacheKey, viewContent);
|
|
9881
|
-
}
|
|
9882
|
-
return this.processTemplate(viewContent, data, viewsConfig);
|
|
9883
|
-
},
|
|
9884
|
-
writable: true,
|
|
9885
|
-
configurable: true
|
|
9886
|
-
},
|
|
9887
|
-
processTemplate: {
|
|
9888
|
-
async value(template, data, viewConfig) {
|
|
9889
|
-
const engine = viewConfig?.engine || "auto";
|
|
9890
|
-
if (engine === "custom" && viewConfig?.customRenderer) {
|
|
9891
|
-
return viewConfig.customRenderer(template, data, {});
|
|
9892
|
-
}
|
|
9893
|
-
let processedTemplate = processHtmlTemplate(template, data);
|
|
9894
|
-
if (viewConfig?.minify?.enabled) {
|
|
9895
|
-
try {
|
|
9896
|
-
const _minifyOptions = viewConfig.minify.options || {
|
|
9897
|
-
removeComments: true,
|
|
9898
|
-
collapseWhitespace: true,
|
|
9899
|
-
minifyJS: true,
|
|
9900
|
-
minifyCSS: true
|
|
9901
|
-
};
|
|
9902
|
-
processedTemplate = processedTemplate.replace(/\s+/g, " ").replace(/<!--[\s\S]*?-->/g, "");
|
|
9903
|
-
} catch (err) {
|
|
9904
|
-
console.error("Error minifying HTML:", err);
|
|
9905
|
-
}
|
|
9906
|
-
}
|
|
9907
|
-
return processedTemplate;
|
|
9908
|
-
},
|
|
9909
|
-
writable: true,
|
|
9910
|
-
configurable: true
|
|
9911
|
-
},
|
|
9912
|
-
view: {
|
|
9913
|
-
value(path, viewOrData, dataOrOptions = {}, optionsOrType = {}, typeOrName = "web", name) {
|
|
9914
|
-
let view;
|
|
9915
|
-
let data = {};
|
|
9916
|
-
let options = {};
|
|
9917
|
-
let type = "web";
|
|
9918
|
-
let routeName;
|
|
9919
|
-
if (typeof viewOrData === "string") {
|
|
9920
|
-
view = viewOrData;
|
|
9921
|
-
if (typeof dataOrOptions === "object" && !Array.isArray(dataOrOptions)) {
|
|
9922
|
-
if ("layout" in dataOrOptions || "status" in dataOrOptions || "headers" in dataOrOptions) {
|
|
9923
|
-
options = dataOrOptions;
|
|
9924
|
-
} else {
|
|
9925
|
-
data = dataOrOptions;
|
|
9926
|
-
}
|
|
9927
|
-
}
|
|
9928
|
-
if (typeof optionsOrType === "object") {
|
|
9929
|
-
options = { ...options, ...optionsOrType };
|
|
9930
|
-
} else if (typeof optionsOrType === "string") {
|
|
9931
|
-
type = optionsOrType;
|
|
9932
|
-
}
|
|
9933
|
-
if (typeof typeOrName === "string") {
|
|
9934
|
-
if (typeOrName === "web" || typeOrName === "api") {
|
|
9935
|
-
type = typeOrName;
|
|
9936
|
-
} else {
|
|
9937
|
-
routeName = typeOrName;
|
|
9938
|
-
}
|
|
9939
|
-
}
|
|
9940
|
-
if (name) {
|
|
9941
|
-
routeName = name;
|
|
9942
|
-
}
|
|
9943
|
-
} else {
|
|
9944
|
-
data = viewOrData;
|
|
9945
|
-
if (typeof dataOrOptions === "string") {
|
|
9946
|
-
view = dataOrOptions;
|
|
9947
|
-
} else {
|
|
9948
|
-
throw new TypeError("View name must be a string");
|
|
9949
|
-
}
|
|
9950
|
-
if (typeof optionsOrType === "object") {
|
|
9951
|
-
options = optionsOrType;
|
|
9952
|
-
} else if (typeof optionsOrType === "string") {
|
|
9953
|
-
if (optionsOrType === "web" || optionsOrType === "api") {
|
|
9954
|
-
type = optionsOrType;
|
|
9955
|
-
} else {
|
|
9956
|
-
view = optionsOrType;
|
|
9957
|
-
type = "web";
|
|
9958
|
-
}
|
|
9959
|
-
}
|
|
9960
|
-
}
|
|
9961
|
-
const handler = async (_req) => {
|
|
9962
|
-
let renderedView = await this.renderView(view, data, {
|
|
9963
|
-
layout: options.layout
|
|
9964
|
-
});
|
|
9965
|
-
const queryPreservationConfig = this.config?.queryPreservation;
|
|
9966
|
-
if (queryPreservationConfig?.enabled !== false && queryPreservationConfig?.preserve?.length) {
|
|
9967
|
-
renderedView = injectQueryPreservationScript(renderedView, queryPreservationConfig);
|
|
9968
|
-
}
|
|
9969
|
-
const headers = new Headers;
|
|
9970
|
-
headers.set("Content-Type", "text/html; charset=utf-8");
|
|
9971
|
-
if (options.headers) {
|
|
9972
|
-
for (const [key, value] of Object.entries(options.headers)) {
|
|
9973
|
-
headers.set(key, value);
|
|
9974
|
-
}
|
|
9975
|
-
}
|
|
9976
|
-
return new Response(renderedView, {
|
|
9977
|
-
status: options.status || 200,
|
|
9978
|
-
headers
|
|
9979
|
-
});
|
|
9980
|
-
};
|
|
9981
|
-
this.get(path, handler, type, routeName);
|
|
9982
|
-
return this;
|
|
9983
|
-
},
|
|
9984
|
-
writable: true,
|
|
9985
|
-
configurable: true
|
|
9986
|
-
}
|
|
9987
|
-
});
|
|
9988
|
-
}
|
|
9989
|
-
|
|
9990
|
-
// src/router/websocket.ts
|
|
9991
|
-
function registerWebSocketHandling(RouterClass) {
|
|
9992
|
-
Object.defineProperties(RouterClass.prototype, {
|
|
9993
|
-
websocket: {
|
|
9994
|
-
value(config2) {
|
|
9995
|
-
this.wsConfig = config2;
|
|
9996
|
-
return this;
|
|
9997
|
-
},
|
|
9998
|
-
writable: true,
|
|
9999
|
-
configurable: true
|
|
10000
|
-
},
|
|
10001
|
-
publish: {
|
|
10002
|
-
value(topic, data, compress = false) {
|
|
10003
|
-
if (!this.serverInstance) {
|
|
10004
|
-
throw new Error("Server not started, cannot publish to WebSocket topics");
|
|
10005
|
-
}
|
|
10006
|
-
return this.serverInstance.publish(topic, data, compress);
|
|
10007
|
-
},
|
|
10008
|
-
writable: true,
|
|
10009
|
-
configurable: true
|
|
10010
|
-
},
|
|
10011
|
-
subscriberCount: {
|
|
10012
|
-
value(topic) {
|
|
10013
|
-
if (!this.serverInstance) {
|
|
10014
|
-
throw new Error("Server not started, cannot get subscriber count");
|
|
10015
|
-
}
|
|
10016
|
-
return this.serverInstance.subscriberCount(topic);
|
|
10017
|
-
},
|
|
10018
|
-
writable: true,
|
|
10019
|
-
configurable: true
|
|
10020
|
-
},
|
|
10021
|
-
upgrade: {
|
|
10022
|
-
value(request2, options) {
|
|
10023
|
-
if (!this.serverInstance) {
|
|
10024
|
-
throw new Error("Server not started, cannot upgrade to WebSocket");
|
|
10025
|
-
}
|
|
10026
|
-
if (!this.wsConfig) {
|
|
10027
|
-
throw new Error("WebSocket configuration not set, use router.websocket() to configure");
|
|
10028
|
-
}
|
|
10029
|
-
return this.serverInstance.upgrade(request2, options);
|
|
10030
|
-
},
|
|
10031
|
-
writable: true,
|
|
10032
|
-
configurable: true
|
|
10033
|
-
},
|
|
10034
|
-
timeout: {
|
|
10035
|
-
value(request2, seconds) {
|
|
10036
|
-
if (!this.serverInstance) {
|
|
10037
|
-
throw new Error("Server not started, cannot set timeout");
|
|
10038
|
-
}
|
|
10039
|
-
this.serverInstance.timeout(request2, seconds);
|
|
10040
|
-
},
|
|
10041
|
-
writable: true,
|
|
10042
|
-
configurable: true
|
|
10043
|
-
},
|
|
10044
|
-
requestIP: {
|
|
10045
|
-
value(request2) {
|
|
10046
|
-
if (!this.serverInstance) {
|
|
10047
|
-
throw new Error("Server not started, cannot get request IP");
|
|
10048
|
-
}
|
|
10049
|
-
return this.serverInstance.requestIP(request2);
|
|
10050
|
-
},
|
|
10051
|
-
writable: true,
|
|
10052
|
-
configurable: true
|
|
10053
|
-
}
|
|
10054
|
-
});
|
|
10055
|
-
}
|
|
10056
9550
|
// src/request/macros.ts
|
|
10057
9551
|
class RequestMacroRegistry {
|
|
10058
9552
|
macros = new Map;
|
|
@@ -10275,91 +9769,599 @@ var BuiltInRequestMacros = {
|
|
|
10275
9769
|
if (key && value) {
|
|
10276
9770
|
cookies[key] = decodeURIComponent(value);
|
|
10277
9771
|
}
|
|
10278
|
-
});
|
|
10279
|
-
}
|
|
10280
|
-
return cookies;
|
|
10281
|
-
},
|
|
10282
|
-
file(name) {
|
|
10283
|
-
return this.files?.find((f) => f.fieldName === name);
|
|
10284
|
-
},
|
|
10285
|
-
hasFile(name) {
|
|
10286
|
-
return !!this.files?.find((f) => f.fieldName === name);
|
|
10287
|
-
},
|
|
10288
|
-
path() {
|
|
10289
|
-
return new URL(this.url).pathname;
|
|
10290
|
-
},
|
|
10291
|
-
fullUrl() {
|
|
10292
|
-
return this.url;
|
|
10293
|
-
},
|
|
10294
|
-
root() {
|
|
10295
|
-
const url2 = new URL(this.url);
|
|
10296
|
-
return `${url2.protocol}//${url2.host}`;
|
|
10297
|
-
},
|
|
10298
|
-
is(pattern) {
|
|
10299
|
-
const path = this.path();
|
|
10300
|
-
const regexPattern = pattern.replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
10301
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
10302
|
-
return regex.test(path);
|
|
10303
|
-
},
|
|
10304
|
-
route() {
|
|
10305
|
-
return this.routeName || null;
|
|
10306
|
-
},
|
|
10307
|
-
fingerprint() {
|
|
10308
|
-
const url2 = new URL(this.url);
|
|
10309
|
-
const data = {
|
|
10310
|
-
method: this.method,
|
|
10311
|
-
path: url2.pathname,
|
|
10312
|
-
query: url2.search,
|
|
10313
|
-
ip: BuiltInRequestMacros.ip.call(this)
|
|
10314
|
-
};
|
|
10315
|
-
return btoa(JSON.stringify(data));
|
|
10316
|
-
},
|
|
10317
|
-
signature(secret) {
|
|
10318
|
-
const data = `${this.method}${this.url}${BuiltInRequestMacros.ip.call(this)}${secret}`;
|
|
10319
|
-
let hash = 0;
|
|
10320
|
-
for (let i = 0;i < data.length; i++) {
|
|
10321
|
-
const char = data.charCodeAt(i);
|
|
10322
|
-
hash = (hash << 5) - hash + char;
|
|
10323
|
-
hash = hash & hash;
|
|
9772
|
+
});
|
|
9773
|
+
}
|
|
9774
|
+
return cookies;
|
|
9775
|
+
},
|
|
9776
|
+
file(name) {
|
|
9777
|
+
return this.files?.find((f) => f.fieldName === name);
|
|
9778
|
+
},
|
|
9779
|
+
hasFile(name) {
|
|
9780
|
+
return !!this.files?.find((f) => f.fieldName === name);
|
|
9781
|
+
},
|
|
9782
|
+
path() {
|
|
9783
|
+
return new URL(this.url).pathname;
|
|
9784
|
+
},
|
|
9785
|
+
fullUrl() {
|
|
9786
|
+
return this.url;
|
|
9787
|
+
},
|
|
9788
|
+
root() {
|
|
9789
|
+
const url2 = new URL(this.url);
|
|
9790
|
+
return `${url2.protocol}//${url2.host}`;
|
|
9791
|
+
},
|
|
9792
|
+
is(pattern) {
|
|
9793
|
+
const path = this.path();
|
|
9794
|
+
const regexPattern = pattern.replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
9795
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
9796
|
+
return regex.test(path);
|
|
9797
|
+
},
|
|
9798
|
+
route() {
|
|
9799
|
+
return this.routeName || null;
|
|
9800
|
+
},
|
|
9801
|
+
fingerprint() {
|
|
9802
|
+
const url2 = new URL(this.url);
|
|
9803
|
+
const data = {
|
|
9804
|
+
method: this.method,
|
|
9805
|
+
path: url2.pathname,
|
|
9806
|
+
query: url2.search,
|
|
9807
|
+
ip: BuiltInRequestMacros.ip.call(this)
|
|
9808
|
+
};
|
|
9809
|
+
return btoa(JSON.stringify(data));
|
|
9810
|
+
},
|
|
9811
|
+
signature(secret) {
|
|
9812
|
+
const data = `${this.method}${this.url}${BuiltInRequestMacros.ip.call(this)}${secret}`;
|
|
9813
|
+
let hash = 0;
|
|
9814
|
+
for (let i = 0;i < data.length; i++) {
|
|
9815
|
+
const char = data.charCodeAt(i);
|
|
9816
|
+
hash = (hash << 5) - hash + char;
|
|
9817
|
+
hash = hash & hash;
|
|
9818
|
+
}
|
|
9819
|
+
return Math.abs(hash).toString(36);
|
|
9820
|
+
},
|
|
9821
|
+
merge(data) {
|
|
9822
|
+
if (!this.validated) {
|
|
9823
|
+
this.validated = {};
|
|
9824
|
+
}
|
|
9825
|
+
Object.assign(this.validated, data);
|
|
9826
|
+
},
|
|
9827
|
+
replace(data) {
|
|
9828
|
+
this.validated = { ...data };
|
|
9829
|
+
},
|
|
9830
|
+
age() {
|
|
9831
|
+
const startTime = this.startTime || Date.now();
|
|
9832
|
+
return Date.now() - startTime;
|
|
9833
|
+
},
|
|
9834
|
+
isFromTrustedProxy(trustedProxies = []) {
|
|
9835
|
+
const ip = BuiltInRequestMacros.ip.call(this);
|
|
9836
|
+
return trustedProxies.includes(ip);
|
|
9837
|
+
},
|
|
9838
|
+
contentLength() {
|
|
9839
|
+
const length = this.headers.get("content-length");
|
|
9840
|
+
return length ? Number.parseInt(length, 10) : 0;
|
|
9841
|
+
},
|
|
9842
|
+
contentType() {
|
|
9843
|
+
return this.headers.get("content-type");
|
|
9844
|
+
},
|
|
9845
|
+
isContentType(type) {
|
|
9846
|
+
const contentType = this.contentType();
|
|
9847
|
+
return contentType ? contentType.includes(type) : false;
|
|
9848
|
+
}
|
|
9849
|
+
};
|
|
9850
|
+
function registerBuiltInRequestMacros() {
|
|
9851
|
+
Object.entries(BuiltInRequestMacros).forEach(([name, handler]) => {
|
|
9852
|
+
RequestWithMacros.macro(name, handler);
|
|
9853
|
+
});
|
|
9854
|
+
}
|
|
9855
|
+
registerBuiltInRequestMacros();
|
|
9856
|
+
|
|
9857
|
+
// src/router/server.ts
|
|
9858
|
+
function registerServerHandling(RouterClass) {
|
|
9859
|
+
Object.defineProperties(RouterClass.prototype, {
|
|
9860
|
+
serve: {
|
|
9861
|
+
async value(options = {}) {
|
|
9862
|
+
if (this._initFileRoutes) {
|
|
9863
|
+
await this._initFileRoutes();
|
|
9864
|
+
}
|
|
9865
|
+
if (this._initApiRoutes) {
|
|
9866
|
+
await this._initApiRoutes();
|
|
9867
|
+
}
|
|
9868
|
+
this.invalidateCache();
|
|
9869
|
+
const serverOptions = {
|
|
9870
|
+
idleTimeout: 255,
|
|
9871
|
+
...options,
|
|
9872
|
+
fetch: this.handleRequest.bind(this)
|
|
9873
|
+
};
|
|
9874
|
+
const staticRouteMap = {};
|
|
9875
|
+
if (this.staticResponses && this.staticResponses.size > 0) {
|
|
9876
|
+
for (const [path, response2] of this.staticResponses) {
|
|
9877
|
+
staticRouteMap[path] = response2;
|
|
9878
|
+
}
|
|
9879
|
+
}
|
|
9880
|
+
if (options.static) {
|
|
9881
|
+
Object.assign(staticRouteMap, options.static);
|
|
9882
|
+
}
|
|
9883
|
+
if (Object.keys(staticRouteMap).length > 0) {
|
|
9884
|
+
serverOptions.static = staticRouteMap;
|
|
9885
|
+
}
|
|
9886
|
+
if (options.development !== undefined) {
|
|
9887
|
+
serverOptions.development = options.development;
|
|
9888
|
+
}
|
|
9889
|
+
if (this.wsConfig) {
|
|
9890
|
+
serverOptions.websocket = this.wsConfig;
|
|
9891
|
+
}
|
|
9892
|
+
this.serverInstance = Bun.serve(serverOptions);
|
|
9893
|
+
if (this.config.verbose) {
|
|
9894
|
+
const port = this.serverInstance.port;
|
|
9895
|
+
const hostname = this.serverInstance.hostname;
|
|
9896
|
+
console.log(`
|
|
9897
|
+
\uD83D\uDE80 Server running at http://${hostname}:${port}
|
|
9898
|
+
`);
|
|
9899
|
+
}
|
|
9900
|
+
return this.serverInstance;
|
|
9901
|
+
},
|
|
9902
|
+
writable: true,
|
|
9903
|
+
configurable: true
|
|
9904
|
+
},
|
|
9905
|
+
reload: {
|
|
9906
|
+
async value() {
|
|
9907
|
+
if (!this.serverInstance) {
|
|
9908
|
+
throw new Error("Server not started, cannot reload");
|
|
9909
|
+
}
|
|
9910
|
+
this.invalidateCache();
|
|
9911
|
+
const port = this.serverInstance.port;
|
|
9912
|
+
const hostname = this.serverInstance.hostname;
|
|
9913
|
+
this.serverInstance.stop();
|
|
9914
|
+
this.serverInstance = Bun.serve({
|
|
9915
|
+
port,
|
|
9916
|
+
hostname,
|
|
9917
|
+
fetch: this.handleRequest.bind(this),
|
|
9918
|
+
websocket: this.wsConfig || undefined
|
|
9919
|
+
});
|
|
9920
|
+
if (this.config.verbose) {
|
|
9921
|
+
console.log(`\uD83D\uDD04 Server reloaded at http://${hostname}:${port}`);
|
|
9922
|
+
}
|
|
9923
|
+
},
|
|
9924
|
+
writable: true,
|
|
9925
|
+
configurable: true
|
|
9926
|
+
},
|
|
9927
|
+
handleRequest: {
|
|
9928
|
+
async value(req) {
|
|
9929
|
+
const self = this;
|
|
9930
|
+
return runWithRequest(req, () => self.handleRequestImpl(req));
|
|
9931
|
+
},
|
|
9932
|
+
writable: true,
|
|
9933
|
+
configurable: true
|
|
9934
|
+
},
|
|
9935
|
+
handleRequestImpl: {
|
|
9936
|
+
async value(req) {
|
|
9937
|
+
try {
|
|
9938
|
+
const url2 = new URL(req.url);
|
|
9939
|
+
if (req.method === "OPTIONS") {
|
|
9940
|
+
const hostname2 = url2.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
|
|
9941
|
+
const optionsMatch = this.matchRoute(url2.pathname, "OPTIONS", hostname2);
|
|
9942
|
+
if (!optionsMatch) {
|
|
9943
|
+
return new Response(null, {
|
|
9944
|
+
status: 204,
|
|
9945
|
+
headers: {
|
|
9946
|
+
"Access-Control-Allow-Origin": "*",
|
|
9947
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
9948
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With, Accept, Origin",
|
|
9949
|
+
"Access-Control-Max-Age": "86400",
|
|
9950
|
+
"Access-Control-Allow-Credentials": "true"
|
|
9951
|
+
}
|
|
9952
|
+
});
|
|
9953
|
+
}
|
|
9954
|
+
}
|
|
9955
|
+
const hostname = url2.hostname || req.headers.get("host")?.split(":")[0] || "localhost";
|
|
9956
|
+
const match = this.matchRoute(url2.pathname, req.method, hostname);
|
|
9957
|
+
const enhancedReq = this.enhanceRequest(req, match?.params || {});
|
|
9958
|
+
setCurrentRequest(enhancedReq);
|
|
9959
|
+
if (match) {
|
|
9960
|
+
enhancedReq.route = match.route;
|
|
9961
|
+
const middlewareStack = [...this.globalMiddleware];
|
|
9962
|
+
if (match.route.middleware && match.route.middleware.length > 0) {
|
|
9963
|
+
middlewareStack.push(...match.route.middleware);
|
|
9964
|
+
}
|
|
9965
|
+
const routeHandlerMiddleware = async (req2, _next) => {
|
|
9966
|
+
return await this.resolveHandler(match.route.handler, req2);
|
|
9967
|
+
};
|
|
9968
|
+
middlewareStack.push(routeHandlerMiddleware);
|
|
9969
|
+
const response2 = await this.runMiddleware(enhancedReq, middlewareStack);
|
|
9970
|
+
if (response2) {
|
|
9971
|
+
return this.applyModifiedCookies(response2, enhancedReq);
|
|
9972
|
+
}
|
|
9973
|
+
return new Response("No response from middleware chain", { status: 500 });
|
|
9974
|
+
}
|
|
9975
|
+
const allowedMethods = this.getAllowedMethods(url2.pathname, hostname);
|
|
9976
|
+
const corsHeaders = {
|
|
9977
|
+
"Content-Type": "application/json",
|
|
9978
|
+
"Access-Control-Allow-Origin": "*",
|
|
9979
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
9980
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With, Accept, Origin"
|
|
9981
|
+
};
|
|
9982
|
+
if (allowedMethods.length > 0) {
|
|
9983
|
+
const methodNotAllowedHandler = async (_req, _next) => {
|
|
9984
|
+
return new Response(JSON.stringify({
|
|
9985
|
+
success: false,
|
|
9986
|
+
message: "Method Not Allowed",
|
|
9987
|
+
path: url2.pathname,
|
|
9988
|
+
method: req.method,
|
|
9989
|
+
allowed: allowedMethods
|
|
9990
|
+
}), {
|
|
9991
|
+
status: 405,
|
|
9992
|
+
headers: { ...corsHeaders, Allow: allowedMethods.join(", ") }
|
|
9993
|
+
});
|
|
9994
|
+
};
|
|
9995
|
+
if (this.globalMiddleware.length > 0) {
|
|
9996
|
+
const stack = [...this.globalMiddleware, methodNotAllowedHandler];
|
|
9997
|
+
const response2 = await this.runMiddleware(enhancedReq, stack);
|
|
9998
|
+
if (response2)
|
|
9999
|
+
return this.applyModifiedCookies(response2, enhancedReq);
|
|
10000
|
+
}
|
|
10001
|
+
return await methodNotAllowedHandler(enhancedReq, async () => null);
|
|
10002
|
+
}
|
|
10003
|
+
if (this.fallbackHandler) {
|
|
10004
|
+
const response2 = await this.resolveHandler(this.fallbackHandler, enhancedReq);
|
|
10005
|
+
return this.applyModifiedCookies(response2, enhancedReq);
|
|
10006
|
+
}
|
|
10007
|
+
const notFoundHandler = async (_req, _next) => {
|
|
10008
|
+
return new Response(JSON.stringify({
|
|
10009
|
+
success: false,
|
|
10010
|
+
message: "Not Found",
|
|
10011
|
+
path: url2.pathname,
|
|
10012
|
+
method: req.method
|
|
10013
|
+
}), { status: 404, headers: corsHeaders });
|
|
10014
|
+
};
|
|
10015
|
+
if (this.globalMiddleware.length > 0) {
|
|
10016
|
+
const stack = [...this.globalMiddleware, notFoundHandler];
|
|
10017
|
+
const response2 = await this.runMiddleware(enhancedReq, stack);
|
|
10018
|
+
if (response2)
|
|
10019
|
+
return this.applyModifiedCookies(response2, enhancedReq);
|
|
10020
|
+
}
|
|
10021
|
+
return await notFoundHandler(enhancedReq, async () => null);
|
|
10022
|
+
} catch (error) {
|
|
10023
|
+
console.error("Error handling request:", error);
|
|
10024
|
+
if (this.errorHandler) {
|
|
10025
|
+
return this.errorHandler(error);
|
|
10026
|
+
}
|
|
10027
|
+
return new Response(JSON.stringify({
|
|
10028
|
+
success: false,
|
|
10029
|
+
message: "Internal Server Error",
|
|
10030
|
+
error: error instanceof Error ? error.message : String(error)
|
|
10031
|
+
}), {
|
|
10032
|
+
status: 500,
|
|
10033
|
+
headers: {
|
|
10034
|
+
"Content-Type": "application/json",
|
|
10035
|
+
"Access-Control-Allow-Origin": "*",
|
|
10036
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
10037
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With, Accept, Origin"
|
|
10038
|
+
}
|
|
10039
|
+
});
|
|
10040
|
+
}
|
|
10041
|
+
},
|
|
10042
|
+
writable: true,
|
|
10043
|
+
configurable: true
|
|
10044
|
+
},
|
|
10045
|
+
enhanceRequest: {
|
|
10046
|
+
value(req, params = {}) {
|
|
10047
|
+
let parsedCookies = null;
|
|
10048
|
+
const getCookies = () => {
|
|
10049
|
+
if (parsedCookies === null) {
|
|
10050
|
+
parsedCookies = {};
|
|
10051
|
+
const cookieHeader = req.headers.get("cookie") || "";
|
|
10052
|
+
cookieHeader.split(";").forEach((cookie) => {
|
|
10053
|
+
const parts = cookie.trim().split("=");
|
|
10054
|
+
if (parts.length >= 2) {
|
|
10055
|
+
const name = parts[0].trim();
|
|
10056
|
+
const value = parts.slice(1).join("=").trim();
|
|
10057
|
+
parsedCookies[name] = decodeURIComponent(value);
|
|
10058
|
+
}
|
|
10059
|
+
});
|
|
10060
|
+
}
|
|
10061
|
+
return parsedCookies;
|
|
10062
|
+
};
|
|
10063
|
+
const cookies = {
|
|
10064
|
+
get: (name) => getCookies()[name],
|
|
10065
|
+
set: (name, value, options = {}) => {
|
|
10066
|
+
const enhancedRequest = req;
|
|
10067
|
+
if (!enhancedRequest._cookiesToSet) {
|
|
10068
|
+
enhancedRequest._cookiesToSet = [];
|
|
10069
|
+
}
|
|
10070
|
+
enhancedRequest._cookiesToSet.push({ name, value, options });
|
|
10071
|
+
},
|
|
10072
|
+
delete: (name, options = {}) => {
|
|
10073
|
+
const enhancedRequest = req;
|
|
10074
|
+
if (!enhancedRequest._cookiesToDelete) {
|
|
10075
|
+
enhancedRequest._cookiesToDelete = [];
|
|
10076
|
+
}
|
|
10077
|
+
enhancedRequest._cookiesToDelete.push({ name, options });
|
|
10078
|
+
},
|
|
10079
|
+
getAll: () => ({ ...getCookies() })
|
|
10080
|
+
};
|
|
10081
|
+
const enhancedReq = Object.assign(req, {
|
|
10082
|
+
params,
|
|
10083
|
+
cookies: getCookies(),
|
|
10084
|
+
_cookiesToSet: [],
|
|
10085
|
+
_cookiesToDelete: []
|
|
10086
|
+
});
|
|
10087
|
+
RequestWithMacros.applyMacros(enhancedReq);
|
|
10088
|
+
Object.assign(enhancedReq, { cookies: { ...getCookies(), ...cookies } });
|
|
10089
|
+
return enhancedReq;
|
|
10090
|
+
},
|
|
10091
|
+
writable: true,
|
|
10092
|
+
configurable: true
|
|
10093
|
+
},
|
|
10094
|
+
applyModifiedCookies: {
|
|
10095
|
+
value(response2, req) {
|
|
10096
|
+
const newResponse = new Response(response2.body, {
|
|
10097
|
+
status: response2.status,
|
|
10098
|
+
statusText: response2.statusText,
|
|
10099
|
+
headers: response2.headers
|
|
10100
|
+
});
|
|
10101
|
+
if (req._cookiesToSet && req._cookiesToSet.length > 0) {
|
|
10102
|
+
for (const { name, value, options } of req._cookiesToSet) {
|
|
10103
|
+
const cookieString = this.serializeCookie(name, value, options);
|
|
10104
|
+
newResponse.headers.append("Set-Cookie", cookieString);
|
|
10105
|
+
}
|
|
10106
|
+
}
|
|
10107
|
+
if (req._cookiesToDelete && req._cookiesToDelete.length > 0) {
|
|
10108
|
+
for (const { name, options } of req._cookiesToDelete) {
|
|
10109
|
+
const deletionOptions = {
|
|
10110
|
+
...options,
|
|
10111
|
+
expires: new Date(0),
|
|
10112
|
+
maxAge: 0
|
|
10113
|
+
};
|
|
10114
|
+
const cookieString = this.serializeCookie(name, "", deletionOptions);
|
|
10115
|
+
newResponse.headers.append("Set-Cookie", cookieString);
|
|
10116
|
+
}
|
|
10117
|
+
}
|
|
10118
|
+
return newResponse;
|
|
10119
|
+
},
|
|
10120
|
+
writable: true,
|
|
10121
|
+
configurable: true
|
|
10122
|
+
},
|
|
10123
|
+
serializeCookie: {
|
|
10124
|
+
value(name, value, options = {}) {
|
|
10125
|
+
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
|
|
10126
|
+
if (options.maxAge !== undefined) {
|
|
10127
|
+
cookie += `; Max-Age=${options.maxAge}`;
|
|
10128
|
+
}
|
|
10129
|
+
if (options.expires && options.expires instanceof Date) {
|
|
10130
|
+
cookie += `; Expires=${options.expires.toUTCString()}`;
|
|
10131
|
+
}
|
|
10132
|
+
if (options.path) {
|
|
10133
|
+
cookie += `; Path=${options.path}`;
|
|
10134
|
+
} else {
|
|
10135
|
+
cookie += "; Path=/";
|
|
10136
|
+
}
|
|
10137
|
+
if (options.domain) {
|
|
10138
|
+
cookie += `; Domain=${options.domain}`;
|
|
10139
|
+
}
|
|
10140
|
+
if (options.secure) {
|
|
10141
|
+
cookie += "; Secure";
|
|
10142
|
+
}
|
|
10143
|
+
if (options.httpOnly) {
|
|
10144
|
+
cookie += "; HttpOnly";
|
|
10145
|
+
}
|
|
10146
|
+
if (options.sameSite) {
|
|
10147
|
+
const sameSite = options.sameSite.toLowerCase();
|
|
10148
|
+
cookie += `; SameSite=${sameSite.charAt(0).toUpperCase() + sameSite.slice(1)}`;
|
|
10149
|
+
}
|
|
10150
|
+
return cookie;
|
|
10151
|
+
},
|
|
10152
|
+
writable: true,
|
|
10153
|
+
configurable: true
|
|
10324
10154
|
}
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10155
|
+
});
|
|
10156
|
+
}
|
|
10157
|
+
|
|
10158
|
+
// src/router/view-rendering.ts
|
|
10159
|
+
import { join as join4 } from "path";
|
|
10160
|
+
function registerViewRendering(RouterClass) {
|
|
10161
|
+
Object.defineProperties(RouterClass.prototype, {
|
|
10162
|
+
renderView: {
|
|
10163
|
+
async value(view, data = {}, options = {}) {
|
|
10164
|
+
const viewsConfig = this.config.views || {
|
|
10165
|
+
viewsPath: "resources/views",
|
|
10166
|
+
extensions: [".html", ".stx"],
|
|
10167
|
+
cache: Bun.env.NODE_ENV === "production",
|
|
10168
|
+
engine: "auto"
|
|
10169
|
+
};
|
|
10170
|
+
const cacheKey = `${view}:${options.layout || ""}`;
|
|
10171
|
+
if (viewsConfig.cache && this.templateCache.has(cacheKey)) {
|
|
10172
|
+
return this.processTemplate(this.templateCache.get(cacheKey), data, viewsConfig);
|
|
10173
|
+
}
|
|
10174
|
+
const viewPath = await resolveViewPath(view, viewsConfig.viewsPath, viewsConfig.extensions);
|
|
10175
|
+
if (!viewPath) {
|
|
10176
|
+
throw new Error(`View '${view}' not found`);
|
|
10177
|
+
}
|
|
10178
|
+
let viewContent = await Bun.file(viewPath).text();
|
|
10179
|
+
const layoutName = options.layout || viewsConfig.defaultLayout;
|
|
10180
|
+
if (layoutName) {
|
|
10181
|
+
const layoutPath = await resolveViewPath(join4("layouts", layoutName), viewsConfig.viewsPath, viewsConfig.extensions);
|
|
10182
|
+
if (!layoutPath) {
|
|
10183
|
+
throw new Error(`Layout '${layoutName}' not found`);
|
|
10184
|
+
}
|
|
10185
|
+
const layoutContent = await Bun.file(layoutPath).text();
|
|
10186
|
+
viewContent = layoutContent.replace(/\{\{\s*content\s*\}\}/g, viewContent);
|
|
10187
|
+
}
|
|
10188
|
+
if (viewsConfig.cache) {
|
|
10189
|
+
this.templateCache.set(cacheKey, viewContent);
|
|
10190
|
+
}
|
|
10191
|
+
return this.processTemplate(viewContent, data, viewsConfig);
|
|
10192
|
+
},
|
|
10193
|
+
writable: true,
|
|
10194
|
+
configurable: true
|
|
10195
|
+
},
|
|
10196
|
+
processTemplate: {
|
|
10197
|
+
async value(template, data, viewConfig) {
|
|
10198
|
+
const engine = viewConfig?.engine || "auto";
|
|
10199
|
+
if (engine === "custom" && viewConfig?.customRenderer) {
|
|
10200
|
+
return viewConfig.customRenderer(template, data, {});
|
|
10201
|
+
}
|
|
10202
|
+
let processedTemplate = processHtmlTemplate(template, data);
|
|
10203
|
+
if (viewConfig?.minify?.enabled) {
|
|
10204
|
+
try {
|
|
10205
|
+
const _minifyOptions = viewConfig.minify.options || {
|
|
10206
|
+
removeComments: true,
|
|
10207
|
+
collapseWhitespace: true,
|
|
10208
|
+
minifyJS: true,
|
|
10209
|
+
minifyCSS: true
|
|
10210
|
+
};
|
|
10211
|
+
processedTemplate = processedTemplate.replace(/\s+/g, " ").replace(/<!--[\s\S]*?-->/g, "");
|
|
10212
|
+
} catch (err) {
|
|
10213
|
+
console.error("Error minifying HTML:", err);
|
|
10214
|
+
}
|
|
10215
|
+
}
|
|
10216
|
+
return processedTemplate;
|
|
10217
|
+
},
|
|
10218
|
+
writable: true,
|
|
10219
|
+
configurable: true
|
|
10220
|
+
},
|
|
10221
|
+
view: {
|
|
10222
|
+
value(path, viewOrData, dataOrOptions = {}, optionsOrType = {}, typeOrName = "web", name) {
|
|
10223
|
+
let view;
|
|
10224
|
+
let data = {};
|
|
10225
|
+
let options = {};
|
|
10226
|
+
let type = "web";
|
|
10227
|
+
let routeName;
|
|
10228
|
+
if (typeof viewOrData === "string") {
|
|
10229
|
+
view = viewOrData;
|
|
10230
|
+
if (typeof dataOrOptions === "object" && !Array.isArray(dataOrOptions)) {
|
|
10231
|
+
if ("layout" in dataOrOptions || "status" in dataOrOptions || "headers" in dataOrOptions) {
|
|
10232
|
+
options = dataOrOptions;
|
|
10233
|
+
} else {
|
|
10234
|
+
data = dataOrOptions;
|
|
10235
|
+
}
|
|
10236
|
+
}
|
|
10237
|
+
if (typeof optionsOrType === "object") {
|
|
10238
|
+
options = { ...options, ...optionsOrType };
|
|
10239
|
+
} else if (typeof optionsOrType === "string") {
|
|
10240
|
+
type = optionsOrType;
|
|
10241
|
+
}
|
|
10242
|
+
if (typeof typeOrName === "string") {
|
|
10243
|
+
if (typeOrName === "web" || typeOrName === "api") {
|
|
10244
|
+
type = typeOrName;
|
|
10245
|
+
} else {
|
|
10246
|
+
routeName = typeOrName;
|
|
10247
|
+
}
|
|
10248
|
+
}
|
|
10249
|
+
if (name) {
|
|
10250
|
+
routeName = name;
|
|
10251
|
+
}
|
|
10252
|
+
} else {
|
|
10253
|
+
data = viewOrData;
|
|
10254
|
+
if (typeof dataOrOptions === "string") {
|
|
10255
|
+
view = dataOrOptions;
|
|
10256
|
+
} else {
|
|
10257
|
+
throw new TypeError("View name must be a string");
|
|
10258
|
+
}
|
|
10259
|
+
if (typeof optionsOrType === "object") {
|
|
10260
|
+
options = optionsOrType;
|
|
10261
|
+
} else if (typeof optionsOrType === "string") {
|
|
10262
|
+
if (optionsOrType === "web" || optionsOrType === "api") {
|
|
10263
|
+
type = optionsOrType;
|
|
10264
|
+
} else {
|
|
10265
|
+
view = optionsOrType;
|
|
10266
|
+
type = "web";
|
|
10267
|
+
}
|
|
10268
|
+
}
|
|
10269
|
+
}
|
|
10270
|
+
const handler = async (_req) => {
|
|
10271
|
+
let renderedView = await this.renderView(view, data, {
|
|
10272
|
+
layout: options.layout
|
|
10273
|
+
});
|
|
10274
|
+
const queryPreservationConfig = this.config?.queryPreservation;
|
|
10275
|
+
if (queryPreservationConfig?.enabled !== false && queryPreservationConfig?.preserve?.length) {
|
|
10276
|
+
renderedView = injectQueryPreservationScript(renderedView, queryPreservationConfig);
|
|
10277
|
+
}
|
|
10278
|
+
const headers = new Headers;
|
|
10279
|
+
headers.set("Content-Type", "text/html; charset=utf-8");
|
|
10280
|
+
if (options.headers) {
|
|
10281
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
10282
|
+
headers.set(key, value);
|
|
10283
|
+
}
|
|
10284
|
+
}
|
|
10285
|
+
return new Response(renderedView, {
|
|
10286
|
+
status: options.status || 200,
|
|
10287
|
+
headers
|
|
10288
|
+
});
|
|
10289
|
+
};
|
|
10290
|
+
this.get(path, handler, type, routeName);
|
|
10291
|
+
return this;
|
|
10292
|
+
},
|
|
10293
|
+
writable: true,
|
|
10294
|
+
configurable: true
|
|
10330
10295
|
}
|
|
10331
|
-
Object.assign(this.validated, data);
|
|
10332
|
-
},
|
|
10333
|
-
replace(data) {
|
|
10334
|
-
this.validated = { ...data };
|
|
10335
|
-
},
|
|
10336
|
-
age() {
|
|
10337
|
-
const startTime = this.startTime || Date.now();
|
|
10338
|
-
return Date.now() - startTime;
|
|
10339
|
-
},
|
|
10340
|
-
isFromTrustedProxy(trustedProxies = []) {
|
|
10341
|
-
const ip = BuiltInRequestMacros.ip.call(this);
|
|
10342
|
-
return trustedProxies.includes(ip);
|
|
10343
|
-
},
|
|
10344
|
-
contentLength() {
|
|
10345
|
-
const length = this.headers.get("content-length");
|
|
10346
|
-
return length ? Number.parseInt(length, 10) : 0;
|
|
10347
|
-
},
|
|
10348
|
-
contentType() {
|
|
10349
|
-
return this.headers.get("content-type");
|
|
10350
|
-
},
|
|
10351
|
-
isContentType(type) {
|
|
10352
|
-
const contentType = this.contentType();
|
|
10353
|
-
return contentType ? contentType.includes(type) : false;
|
|
10354
|
-
}
|
|
10355
|
-
};
|
|
10356
|
-
function registerBuiltInRequestMacros() {
|
|
10357
|
-
Object.entries(BuiltInRequestMacros).forEach(([name, handler]) => {
|
|
10358
|
-
RequestWithMacros.macro(name, handler);
|
|
10359
10296
|
});
|
|
10360
10297
|
}
|
|
10361
|
-
registerBuiltInRequestMacros();
|
|
10362
10298
|
|
|
10299
|
+
// src/router/websocket.ts
|
|
10300
|
+
function registerWebSocketHandling(RouterClass) {
|
|
10301
|
+
Object.defineProperties(RouterClass.prototype, {
|
|
10302
|
+
websocket: {
|
|
10303
|
+
value(config2) {
|
|
10304
|
+
this.wsConfig = config2;
|
|
10305
|
+
return this;
|
|
10306
|
+
},
|
|
10307
|
+
writable: true,
|
|
10308
|
+
configurable: true
|
|
10309
|
+
},
|
|
10310
|
+
publish: {
|
|
10311
|
+
value(topic, data, compress = false) {
|
|
10312
|
+
if (!this.serverInstance) {
|
|
10313
|
+
throw new Error("Server not started, cannot publish to WebSocket topics");
|
|
10314
|
+
}
|
|
10315
|
+
return this.serverInstance.publish(topic, data, compress);
|
|
10316
|
+
},
|
|
10317
|
+
writable: true,
|
|
10318
|
+
configurable: true
|
|
10319
|
+
},
|
|
10320
|
+
subscriberCount: {
|
|
10321
|
+
value(topic) {
|
|
10322
|
+
if (!this.serverInstance) {
|
|
10323
|
+
throw new Error("Server not started, cannot get subscriber count");
|
|
10324
|
+
}
|
|
10325
|
+
return this.serverInstance.subscriberCount(topic);
|
|
10326
|
+
},
|
|
10327
|
+
writable: true,
|
|
10328
|
+
configurable: true
|
|
10329
|
+
},
|
|
10330
|
+
upgrade: {
|
|
10331
|
+
value(request2, options) {
|
|
10332
|
+
if (!this.serverInstance) {
|
|
10333
|
+
throw new Error("Server not started, cannot upgrade to WebSocket");
|
|
10334
|
+
}
|
|
10335
|
+
if (!this.wsConfig) {
|
|
10336
|
+
throw new Error("WebSocket configuration not set, use router.websocket() to configure");
|
|
10337
|
+
}
|
|
10338
|
+
return this.serverInstance.upgrade(request2, options);
|
|
10339
|
+
},
|
|
10340
|
+
writable: true,
|
|
10341
|
+
configurable: true
|
|
10342
|
+
},
|
|
10343
|
+
timeout: {
|
|
10344
|
+
value(request2, seconds) {
|
|
10345
|
+
if (!this.serverInstance) {
|
|
10346
|
+
throw new Error("Server not started, cannot set timeout");
|
|
10347
|
+
}
|
|
10348
|
+
this.serverInstance.timeout(request2, seconds);
|
|
10349
|
+
},
|
|
10350
|
+
writable: true,
|
|
10351
|
+
configurable: true
|
|
10352
|
+
},
|
|
10353
|
+
requestIP: {
|
|
10354
|
+
value(request2) {
|
|
10355
|
+
if (!this.serverInstance) {
|
|
10356
|
+
throw new Error("Server not started, cannot get request IP");
|
|
10357
|
+
}
|
|
10358
|
+
return this.serverInstance.requestIP(request2);
|
|
10359
|
+
},
|
|
10360
|
+
writable: true,
|
|
10361
|
+
configurable: true
|
|
10362
|
+
}
|
|
10363
|
+
});
|
|
10364
|
+
}
|
|
10363
10365
|
// src/response/macros.ts
|
|
10364
10366
|
import process4 from "process";
|
|
10365
10367
|
|