bxo 0.0.5-dev.77 → 0.0.5-dev.79

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.
@@ -0,0 +1,93 @@
1
+ import BXO from "../src";
2
+
3
+ async function main() {
4
+ const app = new BXO({ serve: { port: 3000 } });
5
+
6
+ // Route with space in the path
7
+ app.get("/api/resources/Workspace Item", (ctx) => {
8
+ return ctx.json({
9
+ message: "Found Workspace Item resource!",
10
+ path: ctx.request.url,
11
+ pathname: new URL(ctx.request.url).pathname
12
+ });
13
+ });
14
+
15
+ // Route with URL-encoded space
16
+ app.get("/api/resources/Workspace%20Item", (ctx) => {
17
+ return ctx.json({
18
+ message: "Found URL-encoded Workspace Item resource!",
19
+ path: ctx.request.url,
20
+ pathname: new URL(ctx.request.url).pathname
21
+ });
22
+ });
23
+
24
+ // Route with path parameter (recommended approach)
25
+ app.get("/api/resources/:resourceType", (ctx) => {
26
+ return ctx.json({
27
+ message: `Found resource type: ${ctx.params.resourceType}`,
28
+ path: ctx.request.url,
29
+ pathname: new URL(ctx.request.url).pathname,
30
+ params: ctx.params
31
+ });
32
+ });
33
+
34
+ // Route with multiple path parameters including URL-encoded spaces
35
+ app.get("/api/resources/:resourceType/:id", (ctx) => {
36
+ return ctx.json({
37
+ message: `Found resource: ${ctx.params.resourceType} with ID: ${ctx.params.id}`,
38
+ path: ctx.request.url,
39
+ pathname: new URL(ctx.request.url).pathname,
40
+ params: ctx.params
41
+ });
42
+ });
43
+
44
+ // Test route to show the difference
45
+ app.get("/test", (ctx) => {
46
+ return ctx.text(`
47
+ <!DOCTYPE html>
48
+ <html>
49
+ <head>
50
+ <title>URL Encoding Test</title>
51
+ </head>
52
+ <body>
53
+ <h1>URL Encoding Test</h1>
54
+ <p>Test the following URLs:</p>
55
+ <ul>
56
+ <li><a href="/api/resources/Workspace Item">/api/resources/Workspace Item</a> (with space)</li>
57
+ <li><a href="/api/resources/Workspace%20Item">/api/resources/Workspace%20Item</a> (URL encoded)</li>
58
+ <li><a href="/api/resources/My%20Resource">/api/resources/My%20Resource</a> (URL encoded with params)</li>
59
+ <li><a href="/api/resources/Doctype%20Permission/01992af8-1c69-7000-9219-9b83c2feb2d6">/api/resources/Doctype%20Permission/01992af8-1c69-7000-9219-9b83c2feb2d6</a> (URL encoded with ID)</li>
60
+ </ul>
61
+
62
+ <h2>Test with JavaScript fetch:</h2>
63
+ <button onclick="testFetch('/api/resources/Workspace Item')">Test with space</button>
64
+ <button onclick="testFetch('/api/resources/Workspace%20Item')">Test URL encoded</button>
65
+ <button onclick="testFetch('/api/resources/My%20Resource')">Test with params</button>
66
+ <button onclick="testFetch('/api/resources/Doctype%20Permission/01992af8-1c69-7000-9219-9b83c2feb2d6')">Test with ID</button>
67
+
68
+ <div id="result"></div>
69
+
70
+ <script>
71
+ async function testFetch(url) {
72
+ try {
73
+ const response = await fetch(url);
74
+ const data = await response.json();
75
+ document.getElementById('result').innerHTML =
76
+ '<h3>Result:</h3><pre>' + JSON.stringify(data, null, 2) + '</pre>';
77
+ } catch (error) {
78
+ document.getElementById('result').innerHTML =
79
+ '<h3>Error:</h3><pre>' + error.message + '</pre>';
80
+ }
81
+ }
82
+ </script>
83
+ `, 200, {
84
+ "Content-Type": "text/html"
85
+ });
86
+ });
87
+
88
+ app.start();
89
+ console.log(`Server is running on http://localhost:${app.server?.port}`);
90
+ console.log(`Test URL encoding at http://localhost:${app.server?.port}/test`);
91
+ }
92
+
93
+ main().catch(console.error);
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  ".": "./src/index.ts",
6
6
  "./plugins": "./plugins/index.ts"
7
7
  },
8
- "version": "0.0.5-dev.77",
8
+ "version": "0.0.5-dev.79",
9
9
  "type": "module",
10
10
  "devDependencies": {
11
11
  "@types/bun": "latest"
package/src/index.ts CHANGED
@@ -531,7 +531,7 @@ export default class BXO {
531
531
  return this;
532
532
  }
533
533
 
534
- private async dispatch(route: InternalRoute, req: Request): Promise<Response> {
534
+ private async dispatch(route: InternalRoute, req: Request, pathname?: string): Promise<Response> {
535
535
  // Run beforeRequest hooks
536
536
  for (const hook of this.beforeRequestHooks) {
537
537
  try {
@@ -559,7 +559,8 @@ export default class BXO {
559
559
  }
560
560
 
561
561
  const url = new URL(req.url);
562
- const params = this.extractParams(route, url.pathname);
562
+ const actualPathname = pathname || url.pathname;
563
+ const params = this.extractParams(route, actualPathname);
563
564
  let queryObj: any;
564
565
  let bodyObj: any = undefined;
565
566
  const cookieObj = parseCookies(req.headers.get("cookie"));
@@ -799,12 +800,32 @@ export default class BXO {
799
800
  if (h) return await h(req);
800
801
  }
801
802
 
803
+ // 1.5) Try URL-decoded pathname for exact matches
804
+ const decodedPathname = decodeURIComponent(url.pathname);
805
+ if (decodedPathname !== url.pathname) {
806
+ const exactDecoded = nativeRoutes[decodedPathname];
807
+ if (exactDecoded) {
808
+ const h = exactDecoded[method] || exactDecoded["DEFAULT"];
809
+ if (h) return await h(req);
810
+ }
811
+ }
812
+
802
813
  // 2) Fallback to our matcher list
803
814
  for (const r of this.routes) {
804
815
  if (r.matcher === null) continue; // exact paths handled above
805
816
  if (r.method !== method && r.method !== "DEFAULT") continue;
806
817
  const m = url.pathname.match(r.matcher);
807
- if (m) return this.dispatch(r, req);
818
+ if (m) return this.dispatch(r, req, url.pathname);
819
+ }
820
+
821
+ // 2.5) Try URL-decoded pathname for pattern matches
822
+ if (decodedPathname !== url.pathname) {
823
+ for (const r of this.routes) {
824
+ if (r.matcher === null) continue; // exact paths handled above
825
+ if (r.method !== method && r.method !== "DEFAULT") continue;
826
+ const m = decodedPathname.match(r.matcher);
827
+ if (m) return this.dispatch(r, req, decodedPathname);
828
+ }
808
829
  }
809
830
 
810
831
  // Create 404 response
@@ -0,0 +1,19 @@
1
+ import BXO from "./src";
2
+
3
+ const app = new BXO({ serve: { port: 3001 } });
4
+
5
+ // Test the exact scenario you mentioned
6
+ app.get("/api/resources/:resourceType/:id", (ctx) => {
7
+ return ctx.json({
8
+ message: "Success!",
9
+ resourceType: ctx.params.resourceType,
10
+ id: ctx.params.id,
11
+ allParams: ctx.params,
12
+ url: ctx.request.url,
13
+ pathname: new URL(ctx.request.url).pathname
14
+ });
15
+ });
16
+
17
+ app.start();
18
+ console.log(`Test server running on http://localhost:${app.server?.port}`);
19
+ console.log(`Test URL: http://localhost:${app.server?.port}/api/resources/Doctype%20Permission/01992af8-1c69-7000-9219-9b83c2feb2d6`);