@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/index.js CHANGED
@@ -66,7 +66,7 @@ import {
66
66
  signRequest,
67
67
  singleFileUpload,
68
68
  verifySignature
69
- } from "./chunk-jhsk5c5b.js";
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 { status = 200, headers = {}, pretty = false } = options;
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
- return Math.abs(hash).toString(36);
10326
- },
10327
- merge(data) {
10328
- if (!this.validated) {
10329
- this.validated = {};
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