* A very simple router.
*/
-const { METHODS: httpMethods } = require('http');
+const { METHODS: httpMethods } = require('node:http');
const common = require('../common');
const { DingusError, RouterNoPathError, RouterNoMethodError } = require('../errors');
const PathParameter = require('./path-parameter');
paramPrefix: ':',
};
+/**
+ * @typedef {Array<string|PathParameter>} RoutePath
+ * @property {{[method: string]: PathHandler}} kPathMethods (symbol key)
+ */
+/**
+ * @callback HandlerFn
+ */
+/**
+ * @typedef {object} PathHandler
+ * @property {HandlerFn} handler invoked on path match
+ * @property {any[]} handlerArgs passed to handler
+ */
+/**
+ * @typedef MatchedPath
+ * @property {object} pathParams populated param fields
+ * @property {RoutePath=} matchedPath matched path
+ */
+
/**
* A naïve router which maps incoming requests to handler functions
* by way of url path and request method.
* of its constituent parts as strings or objects, for invariant or
* parameterized parts respectively. Each search path is assigned a
* mapping of methods to handler functions.
- *
- * @property {Object} pathsByLength index to registered paths by number of parts
*/
class Router {
+ static kPathMethods = kPathMethods;
+ pathsByLength;
+
/**
- * @param {Object} options
- * @param {Boolean} options.ignoreTrailingSlash discard any trailing slashes when registering and comparing paths (default: false)
- * @param {String} options.paramPrefix prefix of a path part denoting a named parameter when registering paths (default: ':')
+ * @param {object} options options object
+ * @param {boolean} options.ignoreTrailingSlash discard any trailing slashes when registering and comparing paths (default: false)
+ * @param {string} options.paramPrefix prefix of a path part denoting a named parameter when registering paths (default: ':')
*/
constructor(options = {}) {
common.setOptions(this, defaultOptions, options);
- // Keep lists of paths to match, indexed by path length.
+ /**
+ * Keep lists of registered paths to match, indexed by number of path parts.
+ * @type {{[length: number]: RoutePath[]}}
+ */
this.pathsByLength = {
1: [],
};
}
- /**
- * @typedef {Array<String|PathParameter>} Router~RoutePath
- * @property {Object} kPathMethods (symbol key)
- */
/**
* Prepare a path for insertion into search list.
* A route path is an Array of path parts, with a symbolic property of an object mapping method handlers.
- * @param {String} rawPath
- * @returns {Router~RoutePath}
+ * @param {string} rawPath path string
+ * @returns {RoutePath} route path
* @private
*/
_pathToRoutePath(rawPath) {
}
- /*
+ /**
* Convert a path part string to parameter if needed.
* Remove escape from an escaped parameter.
- * @param {String} part
- * @returns {PathParameter|String}
+ * @param {string} part component of a path
+ * @returns {PathParameter|string} component or parameter object
* @private
*/
_pathPartMunge(part) {
/**
* Compare checkPath to fixedPath, no param substitution, params must match.
- * @param {Router~RoutePath} routePath
- * @param {Router~RoutePath} checkPath
- * @returns {Boolean}
+ * @param {RoutePath} routePath route path
+ * @param {RoutePath} checkPath path to match
+ * @returns {boolean} matched
* @private
*/
static _pathCompareExact(routePath, checkPath) {
/**
* Compare routePath to fixedPath, populating params.
- * @param {Router~RoutePath} routePath
- * @param {Array<String>} checkPath
- * @param {Object} returnParams
- * @returns {Boolean}
+ * @param {RoutePath} routePath route path
+ * @param {Array<string>} checkPath request path to check
+ * @param {object} returnParams populated param components on match
+ * @returns {boolean} matched
* @private
*/
static _pathCompareParam(routePath, checkPath, returnParams = {}) {
}
- /**
- * @typedef Router~MatchedPath
- * @property {Object} pathParams populated param fields
- * @property {Router~RoutePath} matchedPath
- */
/**
* Search for an existing path, return matched path and path parameters.
- * @param {Array<String>} matchParts
- * @returns {Router~MatchedPath}
+ * @param {Array<string>} matchParts path components
+ * @returns {MatchedPath} matched or not
* @private
*/
_pathFind(matchParts) {
result.matchedPath = p;
break;
}
+ result.pathParams = {}; // Reset after potential population from failed match.
}
}
return result;
/**
* Return a matching path, no param substitution, params must match
- * @param {Router~RoutePath} routePath
- * @returns {Router~RoutePath=}
+ * @param {RoutePath} routePath path
+ * @returns {RoutePath=} matched
* @private
*/
_pathFindExact(routePath) {
}
- /**
- * @callback Router~HandlerFn
- */
- /**
- * @typedef {Object} Router~PathHandler
- * @property {Router~HandlerFn} handler
- * @property {any[]} handlerArgs
- */
/**
* Insert a new path handler.
- * @param {string|string[]} methods
- * @param {string} urlPath
- * @param {Router~HandlerFn} handler
- * @param {any[]} handlerArgs
+ * @param {string|string[]} methods methods to match for this handler, '*' allowed
+ * @param {string} urlPath request path to match
+ * @param {HandlerFn} handler handler to invoke on match
+ * @param {any[]} handlerArgs additional arguments for handler
*/
on(methods, urlPath, handler, handlerArgs = []) {
const matchParts = this._pathToRoutePath(urlPath);
* arguments, for a requested url.
* Also sets path named-parameters as #params and the matched path as
* #matchedPath on the context.
- * @param {string} method
- * @param {string[]} urlPath
- * @param {object} ctx
- * @returns {Router~PathHandler}
+ * @param {string} method request method
+ * @param {string[]} urlPath request path
+ * @param {object} ctx request context, updated with matched metadata
+ * @returns {PathHandler} matched path
*/
lookup(method, urlPath, ctx = {}) {
const pathParts = urlPath.split('/').map((part) => decodeURIComponent(part));
}
-Router.kPathMethods = kPathMethods;
module.exports = Router;