aboutsummaryrefslogtreecommitdiff
path: root/source/library/router
diff options
context:
space:
mode:
authorAlex <zuedev@gmail.com>2025-03-23 11:58:36 +0000
committerAlex <zuedev@gmail.com>2025-03-23 11:58:36 +0000
commit93a71aa6eee4e835a1830a9e7fc6f62f9980948c (patch)
tree22855145a9d336954eebd8f0fac127d996cbf7a5 /source/library/router
parent5301726732dee6e967b24a8f96176e74c5499144 (diff)
downloadzue.dev-93a71aa6eee4e835a1830a9e7fc6f62f9980948c.tar
zue.dev-93a71aa6eee4e835a1830a9e7fc6f62f9980948c.tar.gz
zue.dev-93a71aa6eee4e835a1830a9e7fc6f62f9980948c.tar.bz2
zue.dev-93a71aa6eee4e835a1830a9e7fc6f62f9980948c.tar.xz
zue.dev-93a71aa6eee4e835a1830a9e7fc6f62f9980948c.zip
refactor(router): reorganize Router class into separate index file and remove legacy router.js
Diffstat (limited to 'source/library/router')
-rw-r--r--source/library/router/index.js107
-rw-r--r--source/library/router/index.md127
2 files changed, 234 insertions, 0 deletions
diff --git a/source/library/router/index.js b/source/library/router/index.js
new file mode 100644
index 0000000..d0914ff
--- /dev/null
+++ b/source/library/router/index.js
@@ -0,0 +1,107 @@
+/*
+ Router class to handle routing of requests based on the request path.
+
+ @param {Request} request - the incoming request object
+ @param {Environment} environment - the environment object
+ @param {Context} context - the context object
+
+ @returns {Response} a new Response object
+*/
+export default class Router {
+ constructor(request, environment, context) {
+ this.request = request;
+ this.environment = environment;
+ this.context = context;
+ this.routes = new Map(); // Use Map for faster lookups
+ this.parameters = {}; // Store extracted route parameters
+ }
+
+ /*
+ Add a new route to the router.
+
+ @param {string} path - the path to match
+ @param {function} handler - the handler function to call if the path matches
+
+ @returns {Router} the router object
+ */
+ add(path, handler) {
+ if (typeof path !== "string") {
+ throw new TypeError("Path must be a string");
+ }
+ if (typeof handler !== "function") {
+ throw new TypeError("Handler must be a function");
+ }
+
+ const normalizedPath = path.replace(/\/+$/, ""); // Normalize path
+ const pathRegex = new RegExp(
+ "^" +
+ normalizedPath
+ .replace(/:[^/]+/g, "([^/]+)") // Convert :param to regex group
+ .replace(/\//g, "\\/") +
+ "$"
+ );
+ this.routes.set(pathRegex, { handler, originalPath: normalizedPath });
+ return this;
+ }
+
+ /*
+ Helper function to respond with a JSON object.
+
+ @param {object} body - the JSON object to respond with
+
+ @returns {Response} a new Response object
+ */
+ respond(body, status = 200) {
+ return new Response(JSON.stringify(body), {
+ status,
+ headers: {
+ "Access-Control-Allow-Origin": "*",
+ "Content-Type": "application/json",
+ },
+ });
+ }
+
+ /*
+ Route the request to the appropriate handler based on the request path.
+
+ @returns {Response} a new Response object
+ */
+ route() {
+ try {
+ const url = new URL(this.request.url);
+ const normalizedPath = url.pathname.replace(/\/+$/, ""); // Remove trailing slashes
+
+ for (const [pathRegex, { handler, originalPath }] of this.routes) {
+ const match = normalizedPath.match(pathRegex);
+ if (match) {
+ // Extract parameters
+ const paramNames = [...originalPath.matchAll(/:([^/]+)/g)].map(
+ (m) => m[1]
+ );
+ this.parameters = paramNames.reduce((params, name, index) => {
+ params[name] = match[index + 1];
+ return params;
+ }, {});
+
+ return handler(this.request, this.environment, this.context);
+ }
+ }
+
+ // Return 404 if route not found
+ return this.respond(
+ {
+ error: `Route not found: ${normalizedPath}`,
+ },
+ 404
+ );
+ } catch (error) {
+ // Handle unexpected errors
+ return this.respond(
+ {
+ error: `Internal Server Error: ${error.message}`,
+ },
+ 500
+ );
+ }
+ }
+}
diff --git a/source/library/router/index.md b/source/library/router/index.md
new file mode 100644
index 0000000..65e01ce
--- /dev/null
+++ b/source/library/router/index.md
@@ -0,0 +1,127 @@
+# Router Class Documentation
+
+The `Router` class is designed to handle routing of HTTP requests based on the request path. It allows you to define routes with dynamic parameters and associate them with handler functions.
+
+## Constructor
+
+### `new Router(request, environment, context)`
+
+Creates a new instance of the `Router` class.
+
+- **Parameters:**
+
+ - `request` (Request): The incoming request object.
+ - `environment` (Environment): The environment object.
+ - `context` (Context): The context object.
+
+- **Returns:** A new `Router` instance.
+
+---
+
+## Methods
+
+### `add(path, handler)`
+
+Adds a new route to the router.
+
+- **Parameters:**
+
+ - `path` (string): The path to match. Dynamic segments can be defined using `:paramName`.
+ - `handler` (function): The handler function to call if the path matches. The handler receives `request`, `environment`, and `context` as arguments.
+
+- **Returns:** The `Router` instance for chaining.
+
+- **Example:**
+ ```javascript
+ router.add("/users/:id", (request, environment, context) => {
+ const userId = router.parameters.id;
+ return new Response(`User ID: ${userId}`);
+ });
+ ```
+
+---
+
+### `respond(body, status = 200)`
+
+Helper function to create a JSON response.
+
+- **Parameters:**
+
+ - `body` (object): The JSON object to respond with.
+ - `status` (number, optional): The HTTP status code. Defaults to `200`.
+
+- **Returns:** A `Response` object.
+
+- **Example:**
+ ```javascript
+ return router.respond({ message: "Success" }, 200);
+ ```
+
+---
+
+### `route()`
+
+Routes the incoming request to the appropriate handler based on the request path.
+
+- **Returns:** A `Response` object.
+
+- **Behavior:**
+
+ - Matches the request path against the registered routes.
+ - Extracts dynamic parameters from the path and stores them in `router.parameters`.
+ - Calls the corresponding handler function.
+ - Returns a `404` response if no route matches.
+ - Returns a `500` response in case of unexpected errors.
+
+- **Example:**
+ ```javascript
+ const response = router.route();
+ ```
+
+---
+
+## Example Usage
+
+```javascript
+import Router from "./router.js";
+
+const router = new Router(request, environment, context);
+
+router
+ .add("/hello", () => {
+ return new Response("Hello, world!");
+ })
+ .add("/users/:id", (request, environment, context) => {
+ const userId = router.parameters.id;
+ return new Response(`User ID: ${userId}`);
+ });
+
+const response = router.route();
+```
+
+---
+
+## Error Handling
+
+- **404 Not Found:** If no route matches the request path, the `Router` responds with:
+
+ ```json
+ {
+ "error": "Route not found: /path"
+ }
+ ```
+
+- **500 Internal Server Error:** If an unexpected error occurs, the `Router` responds with:
+ ```json
+ {
+ "error": "Internal Server Error: error message"
+ }
+ ```
+
+---
+
+## Notes
+
+- Paths are normalized by removing trailing slashes.
+- Dynamic parameters in paths (e.g., `:id`) are extracted and stored in the `parameters` property of the `Router` instance.
+- The `add` method uses regular expressions to match paths efficiently.