e22m4u 1 год назад
Родитель
Сommit
d16abddaa7
3 измененных файлов с 422 добавлено и 465 удалено
  1. 416 457
      dist/cjs/index.cjs
  2. 3 4
      src/index.d.ts
  3. 3 4
      src/index.js

+ 416 - 457
dist/cjs/index.cjs

@@ -1836,7 +1836,6 @@ var require_dist = __commonJS({
 // src/index.js
 var src_exports = {};
 __export(src_exports, {
-  BUFFER_ENCODING_LIST: () => BUFFER_ENCODING_LIST,
   BodyParser: () => BodyParser,
   CookieParser: () => CookieParser,
   DataSender: () => DataSender,
@@ -1855,18 +1854,7 @@ __export(src_exports, {
   RouterOptions: () => RouterOptions,
   TrieRouter: () => TrieRouter,
   UNPARSABLE_MEDIA_TYPES: () => UNPARSABLE_MEDIA_TYPES,
-  createCookieString: () => createCookieString,
-  createDebugger: () => createDebugger,
-  createError: () => createError,
-  fetchRequestBody: () => fetchRequestBody,
-  getRequestPathname: () => getRequestPathname,
-  isPromise: () => isPromise,
-  isReadableStream: () => isReadableStream,
-  isResponseSent: () => isResponseSent,
-  isWritableStream: () => isWritableStream,
-  parseCookie: () => parseCookie,
-  parseJsonBody: () => parseJsonBody,
-  toCamelCase: () => toCamelCase
+  parseJsonBody: () => parseJsonBody
 });
 module.exports = __toCommonJS(src_exports);
 
@@ -2161,23 +2149,6 @@ function fetchRequestBody(req, bodyBytesLimit = 0) {
   });
 }
 
-// src/utils/create-cookie-string.js
-function createCookieString(data) {
-  if (!data || typeof data !== "object" || Array.isArray(data))
-    throw new Errorf(
-      'The first parameter of "createCookieString" should be an Object, but %v given.',
-      data
-    );
-  let cookies = "";
-  for (const key in data) {
-    if (!Object.prototype.hasOwnProperty.call(data, key)) continue;
-    const val = data[key];
-    if (val == null) continue;
-    cookies += `${key}=${val}; `;
-  }
-  return cookies.trim();
-}
-
 // src/utils/get-request-pathname.js
 function getRequestPathname(req) {
   if (!req || typeof req !== "object" || Array.isArray(req) || typeof req.url !== "string") {
@@ -2189,135 +2160,6 @@ function getRequestPathname(req) {
   return (req.url || "/").replace(/\?.*$/, "");
 }
 
-// src/route.js
-var HTTP_METHOD = {
-  GET: "GET",
-  POST: "POST",
-  PUT: "PUT",
-  PATCH: "PATCH",
-  DELETE: "DELETE"
-};
-var debug = createDebugger("route");
-var Route = class {
-  /**
-   * Method.
-   *
-   * @type {string}
-   * @private
-   */
-  _method;
-  /**
-   * Getter of the method.
-   *
-   * @returns {string}
-   */
-  get method() {
-    return this._method;
-  }
-  /**
-   * Path template.
-   *
-   * @type {string}
-   * @private
-   */
-  _path;
-  /**
-   * Getter of the path.
-   *
-   * @returns {string}
-   */
-  get path() {
-    return this._path;
-  }
-  /**
-   * Handler.
-   *
-   * @type {RouteHandler}
-   * @private
-   */
-  _handler;
-  /**
-   * Getter of the handler.
-   *
-   * @returns {*}
-   */
-  get handler() {
-    return this._handler;
-  }
-  /**
-   * Hook registry.
-   *
-   * @type {HookRegistry}
-   * @private
-   */
-  _hookRegistry = new HookRegistry();
-  /**
-   * Getter of the hook registry.
-   *
-   * @returns {HookRegistry}
-   */
-  get hookRegistry() {
-    return this._hookRegistry;
-  }
-  /**
-   * Constructor.
-   *
-   * @param {RouteDefinition} routeDef
-   */
-  constructor(routeDef) {
-    if (!routeDef || typeof routeDef !== "object" || Array.isArray(routeDef))
-      throw new Errorf(
-        "The first parameter of Route.controller should be an Object, but %v given.",
-        routeDef
-      );
-    if (!routeDef.method || typeof routeDef.method !== "string")
-      throw new Errorf(
-        'The option "method" of the Route should be a non-empty String, but %v given.',
-        routeDef.method
-      );
-    this._method = routeDef.method.toUpperCase();
-    if (typeof routeDef.path !== "string")
-      throw new Errorf(
-        'The option "path" of the Route should be a String, but %v given.',
-        routeDef.path
-      );
-    this._path = routeDef.path;
-    if (typeof routeDef.handler !== "function")
-      throw new Errorf(
-        'The option "handler" of the Route should be a Function, but %v given.',
-        routeDef.handler
-      );
-    this._handler = routeDef.handler;
-    if (routeDef.preHandler != null) {
-      const preHandlerHooks = Array.isArray(routeDef.preHandler) ? routeDef.preHandler : [routeDef.preHandler];
-      preHandlerHooks.forEach((hook) => {
-        this._hookRegistry.addHook(HOOK_NAME.PRE_HANDLER, hook);
-      });
-    }
-    if (routeDef.postHandler != null) {
-      const postHandlerHooks = Array.isArray(routeDef.postHandler) ? routeDef.postHandler : [routeDef.postHandler];
-      postHandlerHooks.forEach((hook) => {
-        this._hookRegistry.addHook(HOOK_NAME.POST_HANDLER, hook);
-      });
-    }
-  }
-  /**
-   * Handle request.
-   *
-   * @param {RequestContext} context
-   * @returns {*}
-   */
-  handle(context) {
-    const requestPath = getRequestPathname(context.req);
-    debug(
-      "Invoking the Route handler for the request %s %v.",
-      this.method.toUpperCase(),
-      requestPath
-    );
-    return this._handler(context);
-  }
-};
-
 // node_modules/@e22m4u/js-service/src/errors/invalid-argument-error.js
 var InvalidArgumentError = class extends Errorf {
 };
@@ -2669,261 +2511,132 @@ var HookInvoker = class extends DebuggableService {
   }
 };
 
-// src/parsers/body-parser.js
-var import_http_errors2 = __toESM(require_http_errors(), 1);
-
-// src/router-options.js
-var RouterOptions = class extends DebuggableService {
+// src/route.js
+var HTTP_METHOD = {
+  GET: "GET",
+  POST: "POST",
+  PUT: "PUT",
+  PATCH: "PATCH",
+  DELETE: "DELETE"
+};
+var debug = createDebugger("route");
+var Route = class {
   /**
-   * Request body bytes limit.
+   * Method.
    *
-   * @type {number}
+   * @type {string}
    * @private
    */
-  _requestBodyBytesLimit = 512e3;
-  // 512kb
+  _method;
   /**
-   * Getter of request body bytes limit.
+   * Getter of the method.
    *
-   * @returns {number}
+   * @returns {string}
    */
-  get requestBodyBytesLimit() {
-    return this._requestBodyBytesLimit;
+  get method() {
+    return this._method;
   }
   /**
-   * Set request body bytes limit.
+   * Path template.
    *
-   * @param {number} input
-   * @returns {RouterOptions}
+   * @type {string}
+   * @private
    */
-  setRequestBodyBytesLimit(input) {
-    if (typeof input !== "number" || input < 0)
-      throw new Errorf(
-        'The option "requestBodyBytesLimit" must be a positive Number or 0, but %v given.',
-        input
-      );
-    this._requestBodyBytesLimit = input;
-    return this;
+  _path;
+  /**
+   * Getter of the path.
+   *
+   * @returns {string}
+   */
+  get path() {
+    return this._path;
   }
-};
-
-// src/parsers/body-parser.js
-var METHODS_WITH_BODY = ["POST", "PUT", "PATCH", "DELETE"];
-var UNPARSABLE_MEDIA_TYPES = ["multipart/form-data"];
-var BodyParser = class extends DebuggableService {
   /**
-   * Parsers.
+   * Handler.
    *
-   * @type {{[mime: string]: Function}}
+   * @type {RouteHandler}
+   * @private
    */
-  _parsers = {
-    "text/plain": (v) => String(v),
-    "application/json": parseJsonBody
-  };
+  _handler;
   /**
-   * Set parser.
+   * Getter of the handler.
    *
-   * @param {string} mediaType
-   * @param {Function} parser
-   * @returns {this}
+   * @returns {*}
    */
-  defineParser(mediaType, parser) {
-    if (!mediaType || typeof mediaType !== "string")
-      throw new Errorf(
-        'The parameter "mediaType" of BodyParser.defineParser should be a non-empty String, but %v given.',
-        mediaType
-      );
-    if (!parser || typeof parser !== "function")
-      throw new Errorf(
-        'The parameter "parser" of BodyParser.defineParser should be a Function, but %v given.',
-        parser
-      );
-    this._parsers[mediaType] = parser;
-    return this;
+  get handler() {
+    return this._handler;
   }
   /**
-   * Has parser.
+   * Hook registry.
    *
-   * @param {string} mediaType
-   * @returns {boolean}
+   * @type {HookRegistry}
+   * @private
    */
-  hasParser(mediaType) {
-    if (!mediaType || typeof mediaType !== "string")
-      throw new Errorf(
-        'The parameter "mediaType" of BodyParser.hasParser should be a non-empty String, but %v given.',
-        mediaType
-      );
-    return Boolean(this._parsers[mediaType]);
-  }
+  _hookRegistry = new HookRegistry();
   /**
-   * Delete parser.
+   * Getter of the hook registry.
    *
-   * @param {string} mediaType
-   * @returns {this}
+   * @returns {HookRegistry}
    */
-  deleteParser(mediaType) {
-    if (!mediaType || typeof mediaType !== "string")
-      throw new Errorf(
-        'The parameter "mediaType" of BodyParser.deleteParser should be a non-empty String, but %v given.',
-        mediaType
-      );
-    const parser = this._parsers[mediaType];
-    if (!parser) throw new Errorf("The parser of %v is not found.", mediaType);
-    delete this._parsers[mediaType];
-    return this;
+  get hookRegistry() {
+    return this._hookRegistry;
   }
   /**
-   * Parse.
+   * Constructor.
    *
-   * @param {import('http').IncomingMessage} req
-   * @returns {Promise<*>|undefined}
+   * @param {RouteDefinition} routeDef
    */
-  parse(req) {
-    if (!METHODS_WITH_BODY.includes(req.method.toUpperCase())) {
-      this.debug(
-        "Body parsing was skipped for the %s request.",
-        req.method.toUpperCase()
+  constructor(routeDef) {
+    if (!routeDef || typeof routeDef !== "object" || Array.isArray(routeDef))
+      throw new Errorf(
+        "The first parameter of Route.controller should be an Object, but %v given.",
+        routeDef
       );
-      return;
-    }
-    const contentType = (req.headers["content-type"] || "").replace(
-      /^([^;]+);.*$/,
-      "$1"
-    );
-    if (!contentType) {
-      this.debug(
-        "Body parsing was skipped because the request has no content type."
+    if (!routeDef.method || typeof routeDef.method !== "string")
+      throw new Errorf(
+        'The option "method" of the Route should be a non-empty String, but %v given.',
+        routeDef.method
       );
-      return;
-    }
-    const { mediaType } = parseContentType(contentType);
-    if (!mediaType)
-      throw createError(
-        import_http_errors2.default.BadRequest,
-        'Unable to parse the "content-type" header.'
+    this._method = routeDef.method.toUpperCase();
+    if (typeof routeDef.path !== "string")
+      throw new Errorf(
+        'The option "path" of the Route should be a String, but %v given.',
+        routeDef.path
       );
-    const parser = this._parsers[mediaType];
-    if (!parser) {
-      if (UNPARSABLE_MEDIA_TYPES.includes(mediaType)) {
-        this.debug("Body parsing was skipped for %v.", mediaType);
-        return;
-      }
-      throw createError(
-        import_http_errors2.default.UnsupportedMediaType,
-        "Media type %v is not supported.",
-        mediaType
+    this._path = routeDef.path;
+    if (typeof routeDef.handler !== "function")
+      throw new Errorf(
+        'The option "handler" of the Route should be a Function, but %v given.',
+        routeDef.handler
       );
-    }
-    const bodyBytesLimit = this.getService(RouterOptions).requestBodyBytesLimit;
-    return fetchRequestBody(req, bodyBytesLimit).then((rawBody) => {
-      if (rawBody != null) return parser(rawBody);
-      return rawBody;
-    });
-  }
-};
-function parseJsonBody(input) {
-  if (typeof input !== "string") return void 0;
-  try {
-    return JSON.parse(input);
-  } catch (error) {
-    if (process.env["DEBUG"] || process.env["NODE_ENV"] === "development")
-      console.warn(error);
-    throw createError(import_http_errors2.default.BadRequest, "Unable to parse request body.");
-  }
-}
-
-// src/parsers/query-parser.js
-var import_querystring = __toESM(require("querystring"), 1);
-var QueryParser = class extends DebuggableService {
-  /**
-   * Parse
-   *
-   * @param {import('http').IncomingMessage} req
-   * @returns {object}
-   */
-  parse(req) {
-    const queryStr = req.url.replace(/^[^?]*\??/, "");
-    const query = queryStr ? import_querystring.default.parse(queryStr) : {};
-    const queryKeys = Object.keys(query);
-    if (queryKeys.length) {
-      queryKeys.forEach((key) => {
-        this.debug("The query %v has the value %v.", key, query[key]);
+    this._handler = routeDef.handler;
+    if (routeDef.preHandler != null) {
+      const preHandlerHooks = Array.isArray(routeDef.preHandler) ? routeDef.preHandler : [routeDef.preHandler];
+      preHandlerHooks.forEach((hook) => {
+        this._hookRegistry.addHook(HOOK_NAME.PRE_HANDLER, hook);
       });
-    } else {
-      this.debug(
-        "The request %s %v has no query.",
-        req.method,
-        getRequestPathname(req)
-      );
     }
-    return query;
-  }
-};
-
-// src/parsers/cookie-parser.js
-var CookieParser = class extends DebuggableService {
-  /**
-   * Parse
-   *
-   * @param {import('http').IncomingMessage} req
-   * @returns {object}
-   */
-  parse(req) {
-    const cookieString = req.headers["cookie"] || "";
-    const cookie = parseCookie(cookieString);
-    const cookieKeys = Object.keys(cookie);
-    if (cookieKeys.length) {
-      cookieKeys.forEach((key) => {
-        this.debug("The cookie %v has the value %v.", key, cookie[key]);
+    if (routeDef.postHandler != null) {
+      const postHandlerHooks = Array.isArray(routeDef.postHandler) ? routeDef.postHandler : [routeDef.postHandler];
+      postHandlerHooks.forEach((hook) => {
+        this._hookRegistry.addHook(HOOK_NAME.POST_HANDLER, hook);
       });
-    } else {
-      this.debug(
-        "The request %s %v has no cookie.",
-        req.method,
-        getRequestPathname(req)
-      );
     }
-    return cookie;
   }
-};
-
-// src/parsers/request-parser.js
-var import_http2 = require("http");
-var RequestParser = class extends DebuggableService {
   /**
-   * Parse.
+   * Handle request.
    *
-   * @param {IncomingMessage} req
-   * @returns {Promise<object>|object}
+   * @param {RequestContext} context
+   * @returns {*}
    */
-  parse(req) {
-    if (!(req instanceof import_http2.IncomingMessage))
-      throw new Errorf(
-        "The first argument of RequestParser.parse should be an instance of IncomingMessage, but %v given.",
-        req
-      );
-    const data = {};
-    const promises = [];
-    const parsedQuery = this.getService(QueryParser).parse(req);
-    if (isPromise(parsedQuery)) {
-      promises.push(parsedQuery.then((v) => data.query = v));
-    } else {
-      data.query = parsedQuery;
-    }
-    const parsedCookie = this.getService(CookieParser).parse(req);
-    if (isPromise(parsedCookie)) {
-      promises.push(parsedCookie.then((v) => data.cookie = v));
-    } else {
-      data.cookie = parsedCookie;
-    }
-    const parsedBody = this.getService(BodyParser).parse(req);
-    if (isPromise(parsedBody)) {
-      promises.push(parsedBody.then((v) => data.body = v));
-    } else {
-      data.body = parsedBody;
-    }
-    data.headers = JSON.parse(JSON.stringify(req.headers));
-    return promises.length ? Promise.all(promises).then(() => data) : data;
+  handle(context) {
+    const requestPath = getRequestPathname(context.req);
+    debug(
+      "Invoking the Route handler for the request %s %v.",
+      this.method.toUpperCase(),
+      requestPath
+    );
+    return this._handler(context);
   }
 };
 
@@ -3054,117 +2767,261 @@ var ErrorSender = class extends DebuggableService {
   }
 };
 
-// src/request-context.js
-var RequestContext = class {
-  /**
-   * Service container.
-   *
-   * @type {import('@e22m4u/js-service').ServiceContainer}
-   */
-  container;
+// src/parsers/body-parser.js
+var import_http_errors2 = __toESM(require_http_errors(), 1);
+
+// src/router-options.js
+var RouterOptions = class extends DebuggableService {
   /**
-   * Request.
+   * Request body bytes limit.
    *
-   * @type {import('http').IncomingMessage}
+   * @type {number}
+   * @private
    */
-  req;
+  _requestBodyBytesLimit = 512e3;
+  // 512kb
   /**
-   * Response.
+   * Getter of request body bytes limit.
    *
-   * @type {import('http').ServerResponse}
+   * @returns {number}
    */
-  res;
+  get requestBodyBytesLimit() {
+    return this._requestBodyBytesLimit;
+  }
   /**
-   * Query.
+   * Set request body bytes limit.
    *
-   * @type {object}
+   * @param {number} input
+   * @returns {RouterOptions}
    */
-  query = {};
+  setRequestBodyBytesLimit(input) {
+    if (typeof input !== "number" || input < 0)
+      throw new Errorf(
+        'The option "requestBodyBytesLimit" must be a positive Number or 0, but %v given.',
+        input
+      );
+    this._requestBodyBytesLimit = input;
+    return this;
+  }
+};
+
+// src/parsers/body-parser.js
+var METHODS_WITH_BODY = ["POST", "PUT", "PATCH", "DELETE"];
+var UNPARSABLE_MEDIA_TYPES = ["multipart/form-data"];
+var BodyParser = class extends DebuggableService {
   /**
-   * Path parameters.
+   * Parsers.
    *
-   * @type {object}
+   * @type {{[mime: string]: Function}}
    */
-  params = {};
+  _parsers = {
+    "text/plain": (v) => String(v),
+    "application/json": parseJsonBody
+  };
   /**
-   * Parsed body.
+   * Set parser.
    *
-   * @type {*}
+   * @param {string} mediaType
+   * @param {Function} parser
+   * @returns {this}
    */
-  body;
+  defineParser(mediaType, parser) {
+    if (!mediaType || typeof mediaType !== "string")
+      throw new Errorf(
+        'The parameter "mediaType" of BodyParser.defineParser should be a non-empty String, but %v given.',
+        mediaType
+      );
+    if (!parser || typeof parser !== "function")
+      throw new Errorf(
+        'The parameter "parser" of BodyParser.defineParser should be a Function, but %v given.',
+        parser
+      );
+    this._parsers[mediaType] = parser;
+    return this;
+  }
   /**
-   * Headers.
+   * Has parser.
    *
-   * @type {object}
+   * @param {string} mediaType
+   * @returns {boolean}
    */
-  headers = {};
+  hasParser(mediaType) {
+    if (!mediaType || typeof mediaType !== "string")
+      throw new Errorf(
+        'The parameter "mediaType" of BodyParser.hasParser should be a non-empty String, but %v given.',
+        mediaType
+      );
+    return Boolean(this._parsers[mediaType]);
+  }
   /**
-   * Parsed cookie.
+   * Delete parser.
    *
-   * @type {object}
+   * @param {string} mediaType
+   * @returns {this}
    */
-  cookie = {};
+  deleteParser(mediaType) {
+    if (!mediaType || typeof mediaType !== "string")
+      throw new Errorf(
+        'The parameter "mediaType" of BodyParser.deleteParser should be a non-empty String, but %v given.',
+        mediaType
+      );
+    const parser = this._parsers[mediaType];
+    if (!parser) throw new Errorf("The parser of %v is not found.", mediaType);
+    delete this._parsers[mediaType];
+    return this;
+  }
   /**
-   * Method.
+   * Parse.
    *
-   * @returns {string}
+   * @param {import('http').IncomingMessage} req
+   * @returns {Promise<*>|undefined}
    */
-  get method() {
-    return this.req.method.toUpperCase();
+  parse(req) {
+    if (!METHODS_WITH_BODY.includes(req.method.toUpperCase())) {
+      this.debug(
+        "Body parsing was skipped for the %s request.",
+        req.method.toUpperCase()
+      );
+      return;
+    }
+    const contentType = (req.headers["content-type"] || "").replace(
+      /^([^;]+);.*$/,
+      "$1"
+    );
+    if (!contentType) {
+      this.debug(
+        "Body parsing was skipped because the request has no content type."
+      );
+      return;
+    }
+    const { mediaType } = parseContentType(contentType);
+    if (!mediaType)
+      throw createError(
+        import_http_errors2.default.BadRequest,
+        'Unable to parse the "content-type" header.'
+      );
+    const parser = this._parsers[mediaType];
+    if (!parser) {
+      if (UNPARSABLE_MEDIA_TYPES.includes(mediaType)) {
+        this.debug("Body parsing was skipped for %v.", mediaType);
+        return;
+      }
+      throw createError(
+        import_http_errors2.default.UnsupportedMediaType,
+        "Media type %v is not supported.",
+        mediaType
+      );
+    }
+    const bodyBytesLimit = this.getService(RouterOptions).requestBodyBytesLimit;
+    return fetchRequestBody(req, bodyBytesLimit).then((rawBody) => {
+      if (rawBody != null) return parser(rawBody);
+      return rawBody;
+    });
   }
-  /**
-   * Path.
-   *
-   * @returns {string}
-   */
-  get path() {
-    return this.req.url;
+};
+function parseJsonBody(input) {
+  if (typeof input !== "string") return void 0;
+  try {
+    return JSON.parse(input);
+  } catch (error) {
+    if (process.env["DEBUG"] || process.env["NODE_ENV"] === "development")
+      console.warn(error);
+    throw createError(import_http_errors2.default.BadRequest, "Unable to parse request body.");
   }
+}
+
+// src/parsers/query-parser.js
+var import_querystring = __toESM(require("querystring"), 1);
+var QueryParser = class extends DebuggableService {
   /**
-   * Pathname.
+   * Parse
    *
-   * @type {string|undefined}
-   * @private
+   * @param {import('http').IncomingMessage} req
+   * @returns {object}
    */
-  _pathname = void 0;
+  parse(req) {
+    const queryStr = req.url.replace(/^[^?]*\??/, "");
+    const query = queryStr ? import_querystring.default.parse(queryStr) : {};
+    const queryKeys = Object.keys(query);
+    if (queryKeys.length) {
+      queryKeys.forEach((key) => {
+        this.debug("The query %v has the value %v.", key, query[key]);
+      });
+    } else {
+      this.debug(
+        "The request %s %v has no query.",
+        req.method,
+        getRequestPathname(req)
+      );
+    }
+    return query;
+  }
+};
+
+// src/parsers/cookie-parser.js
+var CookieParser = class extends DebuggableService {
   /**
-   * Pathname.
+   * Parse
    *
-   * @returns {string}
+   * @param {import('http').IncomingMessage} req
+   * @returns {object}
    */
-  get pathname() {
-    if (this._pathname != null) return this._pathname;
-    this._pathname = getRequestPathname(this.req);
-    return this._pathname;
+  parse(req) {
+    const cookieString = req.headers["cookie"] || "";
+    const cookie = parseCookie(cookieString);
+    const cookieKeys = Object.keys(cookie);
+    if (cookieKeys.length) {
+      cookieKeys.forEach((key) => {
+        this.debug("The cookie %v has the value %v.", key, cookie[key]);
+      });
+    } else {
+      this.debug(
+        "The request %s %v has no cookie.",
+        req.method,
+        getRequestPathname(req)
+      );
+    }
+    return cookie;
   }
+};
+
+// src/parsers/request-parser.js
+var import_http2 = require("http");
+var RequestParser = class extends DebuggableService {
   /**
-   * Constructor.
+   * Parse.
    *
-   * @param {ServiceContainer} container
-   * @param {import('http').IncomingMessage} request
-   * @param {import('http').ServerResponse} response
+   * @param {IncomingMessage} req
+   * @returns {Promise<object>|object}
    */
-  constructor(container, request, response) {
-    if (!(container instanceof ServiceContainer))
-      throw new Errorf(
-        'The parameter "container" of RequestContext.constructor should be an instance of ServiceContainer, but %v given.',
-        container
-      );
-    this.container = container;
-    if (!request || typeof request !== "object" || Array.isArray(request) || !isReadableStream(request)) {
+  parse(req) {
+    if (!(req instanceof import_http2.IncomingMessage))
       throw new Errorf(
-        'The parameter "request" of RequestContext.constructor should be an instance of IncomingMessage, but %v given.',
-        request
+        "The first argument of RequestParser.parse should be an instance of IncomingMessage, but %v given.",
+        req
       );
+    const data = {};
+    const promises = [];
+    const parsedQuery = this.getService(QueryParser).parse(req);
+    if (isPromise(parsedQuery)) {
+      promises.push(parsedQuery.then((v) => data.query = v));
+    } else {
+      data.query = parsedQuery;
     }
-    this.req = request;
-    if (!response || typeof response !== "object" || Array.isArray(response) || !isWritableStream(response)) {
-      throw new Errorf(
-        'The parameter "response" of RequestContext.constructor should be an instance of ServerResponse, but %v given.',
-        response
-      );
+    const parsedCookie = this.getService(CookieParser).parse(req);
+    if (isPromise(parsedCookie)) {
+      promises.push(parsedCookie.then((v) => data.cookie = v));
+    } else {
+      data.cookie = parsedCookie;
     }
-    this.res = response;
+    const parsedBody = this.getService(BodyParser).parse(req);
+    if (isPromise(parsedBody)) {
+      promises.push(parsedBody.then((v) => data.body = v));
+    } else {
+      data.body = parsedBody;
+    }
+    data.headers = JSON.parse(JSON.stringify(req.headers));
+    return promises.length ? Promise.all(promises).then(() => data) : data;
   }
 };
 
@@ -3521,6 +3378,120 @@ var RouteRegistry = class extends DebuggableService {
   }
 };
 
+// src/request-context.js
+var RequestContext = class {
+  /**
+   * Service container.
+   *
+   * @type {import('@e22m4u/js-service').ServiceContainer}
+   */
+  container;
+  /**
+   * Request.
+   *
+   * @type {import('http').IncomingMessage}
+   */
+  req;
+  /**
+   * Response.
+   *
+   * @type {import('http').ServerResponse}
+   */
+  res;
+  /**
+   * Query.
+   *
+   * @type {object}
+   */
+  query = {};
+  /**
+   * Path parameters.
+   *
+   * @type {object}
+   */
+  params = {};
+  /**
+   * Parsed body.
+   *
+   * @type {*}
+   */
+  body;
+  /**
+   * Headers.
+   *
+   * @type {object}
+   */
+  headers = {};
+  /**
+   * Parsed cookie.
+   *
+   * @type {object}
+   */
+  cookie = {};
+  /**
+   * Method.
+   *
+   * @returns {string}
+   */
+  get method() {
+    return this.req.method.toUpperCase();
+  }
+  /**
+   * Path.
+   *
+   * @returns {string}
+   */
+  get path() {
+    return this.req.url;
+  }
+  /**
+   * Pathname.
+   *
+   * @type {string|undefined}
+   * @private
+   */
+  _pathname = void 0;
+  /**
+   * Pathname.
+   *
+   * @returns {string}
+   */
+  get pathname() {
+    if (this._pathname != null) return this._pathname;
+    this._pathname = getRequestPathname(this.req);
+    return this._pathname;
+  }
+  /**
+   * Constructor.
+   *
+   * @param {ServiceContainer} container
+   * @param {import('http').IncomingMessage} request
+   * @param {import('http').ServerResponse} response
+   */
+  constructor(container, request, response) {
+    if (!(container instanceof ServiceContainer))
+      throw new Errorf(
+        'The parameter "container" of RequestContext.constructor should be an instance of ServiceContainer, but %v given.',
+        container
+      );
+    this.container = container;
+    if (!request || typeof request !== "object" || Array.isArray(request) || !isReadableStream(request)) {
+      throw new Errorf(
+        'The parameter "request" of RequestContext.constructor should be an instance of IncomingMessage, but %v given.',
+        request
+      );
+    }
+    this.req = request;
+    if (!response || typeof response !== "object" || Array.isArray(response) || !isWritableStream(response)) {
+      throw new Errorf(
+        'The parameter "response" of RequestContext.constructor should be an instance of ServerResponse, but %v given.',
+        response
+      );
+    }
+    this.res = response;
+  }
+};
+
 // src/trie-router.js
 var TrieRouter = class extends DebuggableService {
   /**
@@ -3669,7 +3640,6 @@ var TrieRouter = class extends DebuggableService {
 };
 // Annotate the CommonJS export names for ESM import in node:
 0 && (module.exports = {
-  BUFFER_ENCODING_LIST,
   BodyParser,
   CookieParser,
   DataSender,
@@ -3688,18 +3658,7 @@ var TrieRouter = class extends DebuggableService {
   RouterOptions,
   TrieRouter,
   UNPARSABLE_MEDIA_TYPES,
-  createCookieString,
-  createDebugger,
-  createError,
-  fetchRequestBody,
-  getRequestPathname,
-  isPromise,
-  isReadableStream,
-  isResponseSent,
-  isWritableStream,
-  parseCookie,
-  parseJsonBody,
-  toCamelCase
+  parseJsonBody
 });
 /*! Bundled license information:
 

+ 3 - 4
src/index.d.ts

@@ -1,9 +1,8 @@
+export * from './route.js';
 export * from './hooks/index.js';
+export * from './trie-router.js';
 export * from './parsers/index.js';
 export * from './senders/index.js';
-export * from './utils/index.js';
-export * from './request-context.js';
-export * from './route.js';
 export * from './route-registry.js';
 export * from './router-options.js';
-export * from './trie-router.js';
+export * from './request-context.js';

+ 3 - 4
src/index.js

@@ -1,9 +1,8 @@
+export * from './route.js';
 export * from './hooks/index.js';
+export * from './trie-router.js';
 export * from './parsers/index.js';
 export * from './senders/index.js';
-export * from './utils/index.js';
-export * from './request-context.js';
-export * from './route.js';
 export * from './route-registry.js';
 export * from './router-options.js';
-export * from './trie-router.js';
+export * from './request-context.js';