update devDependencies, add jsdoc lint, fix lint issues
[squeep-api-dingus] / lib / router / index.js
index fd19bdf5192cfd9efd7b4c00023f1ae92265cbd1..975187fa2f759f9a00410d85025374baad5ca230 100644 (file)
@@ -5,7 +5,7 @@
  * 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');
@@ -18,6 +18,24 @@ const defaultOptions = {
   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.
@@ -28,34 +46,34 @@ const defaultOptions = {
  * 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) {
@@ -80,11 +98,11 @@ class Router {
   }
 
 
-  /*
+  /**
    * 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) {
@@ -100,9 +118,9 @@ class Router {
 
   /**
    * 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) {
@@ -126,10 +144,10 @@ class Router {
 
   /**
    * 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 = {}) {
@@ -152,15 +170,10 @@ class Router {
   }
 
 
-  /**
-   * @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) {
@@ -175,6 +188,7 @@ class Router {
           result.matchedPath = p;
           break;
         }
+        result.pathParams = {}; // Reset after potential population from failed match.
       }
     }
     return result;
@@ -183,8 +197,8 @@ class Router {
 
   /**
    * 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) {
@@ -200,20 +214,12 @@ class Router {
   }
 
 
-  /**
-   * @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);
@@ -245,10 +251,10 @@ class Router {
    * 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));
@@ -274,6 +280,5 @@ class Router {
 
 
 }
-Router.kPathMethods = kPathMethods;
 
 module.exports = Router;