index.cjs 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048
  1. "use strict";
  2. var __create = Object.create;
  3. var __defProp = Object.defineProperty;
  4. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  5. var __getOwnPropNames = Object.getOwnPropertyNames;
  6. var __getProtoOf = Object.getPrototypeOf;
  7. var __hasOwnProp = Object.prototype.hasOwnProperty;
  8. var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
  9. var __export = (target, all) => {
  10. for (var name in all)
  11. __defProp(target, name, { get: all[name], enumerable: true });
  12. };
  13. var __copyProps = (to, from, except, desc) => {
  14. if (from && typeof from === "object" || typeof from === "function") {
  15. for (let key of __getOwnPropNames(from))
  16. if (!__hasOwnProp.call(to, key) && key !== except)
  17. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  18. }
  19. return to;
  20. };
  21. var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  22. // If the importer is in node compatibility mode or this is not an ESM
  23. // file that has been converted to a CommonJS file using a Babel-
  24. // compatible transform (i.e. "__esModule" has not been set), then set
  25. // "default" to the CommonJS "module.exports" for node compatibility.
  26. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  27. mod
  28. ));
  29. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  30. // src/index.js
  31. var index_exports = {};
  32. __export(index_exports, {
  33. BodyParser: () => BodyParser,
  34. CHARACTER_ENCODING_LIST: () => CHARACTER_ENCODING_LIST,
  35. CookiesParser: () => CookiesParser,
  36. DataSender: () => DataSender,
  37. EXPOSED_ERROR_PROPERTIES: () => EXPOSED_ERROR_PROPERTIES,
  38. ErrorSender: () => ErrorSender,
  39. HookInvoker: () => HookInvoker,
  40. HookRegistry: () => HookRegistry,
  41. HttpMethod: () => HttpMethod,
  42. METHODS_WITH_BODY: () => METHODS_WITH_BODY,
  43. QueryParser: () => QueryParser,
  44. ROOT_PATH: () => ROOT_PATH,
  45. RequestContext: () => RequestContext,
  46. RequestParser: () => RequestParser,
  47. Route: () => Route,
  48. RouteRegistry: () => RouteRegistry,
  49. RouterHookType: () => RouterHookType,
  50. RouterOptions: () => RouterOptions,
  51. TrieRouter: () => TrieRouter,
  52. UNPARSABLE_MEDIA_TYPES: () => UNPARSABLE_MEDIA_TYPES,
  53. cloneDeep: () => cloneDeep,
  54. createCookieString: () => createCookieString,
  55. createError: () => createError,
  56. createRequestMock: () => createRequestMock,
  57. createResponseMock: () => createResponseMock,
  58. createRouteMock: () => createRouteMock,
  59. fetchRequestBody: () => fetchRequestBody,
  60. getRequestPathname: () => getRequestPathname,
  61. isPromise: () => isPromise,
  62. isReadableStream: () => isReadableStream,
  63. isResponseSent: () => isResponseSent,
  64. isWritableStream: () => isWritableStream,
  65. parseContentType: () => parseContentType,
  66. parseCookieString: () => parseCookieString,
  67. parseJsonBody: () => parseJsonBody,
  68. toCamelCase: () => toCamelCase,
  69. validateRouteDefinition: () => validateRouteDefinition
  70. });
  71. module.exports = __toCommonJS(index_exports);
  72. // src/route/route.js
  73. var import_js_debug = require("@e22m4u/js-debug");
  74. // src/hooks/hook-invoker.js
  75. var import_js_format11 = require("@e22m4u/js-format");
  76. // src/debuggable-service.js
  77. var import_js_service = require("@e22m4u/js-service");
  78. var MODULE_DEBUG_NAMESPACE = "jsTrieRouter";
  79. var _DebuggableService = class _DebuggableService extends import_js_service.DebuggableService {
  80. /**
  81. * Constructor.
  82. *
  83. * @param {ServiceContainer} container
  84. */
  85. constructor(container = void 0) {
  86. super(container, {
  87. namespace: MODULE_DEBUG_NAMESPACE,
  88. noEnvironmentNamespace: true
  89. });
  90. }
  91. };
  92. __name(_DebuggableService, "DebuggableService");
  93. var DebuggableService = _DebuggableService;
  94. // src/utils/clone-deep.js
  95. function cloneDeep(value) {
  96. if (value == null || typeof value !== "object") {
  97. return value;
  98. }
  99. if (value instanceof Date) {
  100. return new Date(value.getTime());
  101. }
  102. if (Array.isArray(value)) {
  103. return value.map((item) => cloneDeep(item));
  104. }
  105. const proto = Object.getPrototypeOf(value);
  106. if (proto === Object.prototype || proto === null) {
  107. const newObj = proto === null ? /* @__PURE__ */ Object.create(null) : {};
  108. for (const key in value) {
  109. if (Object.prototype.hasOwnProperty.call(value, key)) {
  110. newObj[key] = cloneDeep(value[key]);
  111. }
  112. }
  113. return newObj;
  114. }
  115. return value;
  116. }
  117. __name(cloneDeep, "cloneDeep");
  118. // src/utils/is-promise.js
  119. function isPromise(value) {
  120. if (!value) {
  121. return false;
  122. }
  123. if (typeof value !== "object") {
  124. return false;
  125. }
  126. return typeof value.then === "function";
  127. }
  128. __name(isPromise, "isPromise");
  129. // src/utils/create-error.js
  130. var import_js_format = require("@e22m4u/js-format");
  131. function createError(errorCtor, message, ...args) {
  132. if (typeof errorCtor !== "function") {
  133. throw new import_js_format.InvalidArgumentError(
  134. 'The first parameter of "createError" must be a constructor, but %v was given.',
  135. errorCtor
  136. );
  137. }
  138. if (message != null && typeof message !== "string") {
  139. throw new import_js_format.InvalidArgumentError(
  140. 'The second parameter of "createError" must be a String, but %v was given.',
  141. message
  142. );
  143. }
  144. if (message == null) {
  145. return new errorCtor();
  146. }
  147. const interpolatedMessage = (0, import_js_format.format)(message, ...args);
  148. return new errorCtor(interpolatedMessage);
  149. }
  150. __name(createError, "createError");
  151. // src/utils/to-camel-case.js
  152. var import_js_format2 = require("@e22m4u/js-format");
  153. function toCamelCase(input) {
  154. if (typeof input !== "string") {
  155. throw new import_js_format2.InvalidArgumentError(
  156. 'The first parameter of "toCamelCase" must be a String, but %v was given.',
  157. input
  158. );
  159. }
  160. return input.replace(/(^\w|[A-Z]|\b\w)/g, (c) => c.toUpperCase()).replace(/\W+/g, "").replace(/(^\w)/g, (c) => c.toLowerCase());
  161. }
  162. __name(toCamelCase, "toCamelCase");
  163. // src/utils/is-response-sent.js
  164. var import_js_format3 = require("@e22m4u/js-format");
  165. function isResponseSent(response) {
  166. if (!response || typeof response !== "object" || Array.isArray(response) || typeof response.headersSent !== "boolean") {
  167. throw new import_js_format3.InvalidArgumentError(
  168. 'The first parameter of "isResponseSent" must be an instance of ServerResponse, but %v was given.',
  169. response
  170. );
  171. }
  172. return response.headersSent;
  173. }
  174. __name(isResponseSent, "isResponseSent");
  175. // src/utils/create-route-mock.js
  176. function createRouteMock(options = {}) {
  177. return new Route({
  178. method: options.method || HttpMethod.GET,
  179. path: options.path || "/",
  180. handler: options.handler || (() => "OK")
  181. });
  182. }
  183. __name(createRouteMock, "createRouteMock");
  184. // src/utils/is-readable-stream.js
  185. function isReadableStream(value) {
  186. if (!value || typeof value !== "object") {
  187. return false;
  188. }
  189. return typeof value.pipe === "function";
  190. }
  191. __name(isReadableStream, "isReadableStream");
  192. // src/utils/parse-content-type.js
  193. var import_js_format4 = require("@e22m4u/js-format");
  194. function parseContentType(input) {
  195. if (typeof input !== "string") {
  196. throw new import_js_format4.InvalidArgumentError(
  197. "The first parameter of `parseContentType` must be a String, but %v was given.",
  198. input
  199. );
  200. }
  201. const res = { mediaType: void 0, charset: void 0, boundary: void 0 };
  202. const re = /^\s*([^\s;/]+\/[^\s;/]+)(?:;\s*charset=([^\s;]+))?(?:;\s*boundary=([^\s;]+))?.*$/i;
  203. const matches = re.exec(input);
  204. if (matches && matches[1]) {
  205. res.mediaType = matches[1];
  206. if (matches[2]) {
  207. res.charset = matches[2];
  208. }
  209. if (matches[3]) {
  210. res.boundary = matches[3];
  211. }
  212. }
  213. return res;
  214. }
  215. __name(parseContentType, "parseContentType");
  216. // src/utils/is-writable-stream.js
  217. function isWritableStream(value) {
  218. if (!value || typeof value !== "object") {
  219. return false;
  220. }
  221. return typeof value.end === "function";
  222. }
  223. __name(isWritableStream, "isWritableStream");
  224. // src/utils/fetch-request-body.js
  225. var import_http_errors = __toESM(require("http-errors"), 1);
  226. var import_http = require("http");
  227. var import_js_format5 = require("@e22m4u/js-format");
  228. var CHARACTER_ENCODING_LIST = [
  229. "ascii",
  230. "utf8",
  231. "utf-8",
  232. "utf16le",
  233. "utf-16le",
  234. "ucs2",
  235. "ucs-2",
  236. "latin1"
  237. ];
  238. function fetchRequestBody(request, bodyBytesLimit = 0) {
  239. if (!(request instanceof import_http.IncomingMessage)) {
  240. throw new import_js_format5.InvalidArgumentError(
  241. 'The first parameter of "fetchRequestBody" must be an IncomingMessage instance, but %v was given.',
  242. request
  243. );
  244. }
  245. if (typeof bodyBytesLimit !== "number") {
  246. throw new import_js_format5.InvalidArgumentError(
  247. 'The parameter "bodyBytesLimit" of "fetchRequestBody" must be a number, but %v was given.',
  248. bodyBytesLimit
  249. );
  250. }
  251. return new Promise((resolve, reject) => {
  252. const contentLength = parseInt(
  253. request.headers["content-length"] || "0",
  254. 10
  255. );
  256. if (bodyBytesLimit && contentLength && contentLength > bodyBytesLimit) {
  257. throw createError(
  258. import_http_errors.default.PayloadTooLarge,
  259. "Request body limit is %s bytes, but %s bytes given.",
  260. bodyBytesLimit,
  261. contentLength
  262. );
  263. }
  264. let encoding = "utf-8";
  265. const contentType = request.headers["content-type"] || "";
  266. if (contentType) {
  267. const parsedContentType = parseContentType(contentType);
  268. if (parsedContentType && parsedContentType.charset) {
  269. encoding = parsedContentType.charset.toLowerCase();
  270. if (!CHARACTER_ENCODING_LIST.includes(encoding)) {
  271. throw createError(
  272. import_http_errors.default.UnsupportedMediaType,
  273. "Request encoding %v is not supported.",
  274. encoding
  275. );
  276. }
  277. }
  278. }
  279. const data = [];
  280. let receivedLength = 0;
  281. const onData = /* @__PURE__ */ __name((chunk) => {
  282. receivedLength += chunk.length;
  283. if (bodyBytesLimit && receivedLength > bodyBytesLimit) {
  284. request.removeAllListeners();
  285. const error = createError(
  286. import_http_errors.default.PayloadTooLarge,
  287. "Request body limit is %v bytes, but %v bytes given.",
  288. bodyBytesLimit,
  289. receivedLength
  290. );
  291. reject(error);
  292. return;
  293. }
  294. data.push(chunk);
  295. }, "onData");
  296. const onEnd = /* @__PURE__ */ __name(() => {
  297. request.removeAllListeners();
  298. if (contentLength && contentLength !== receivedLength) {
  299. const error = createError(
  300. import_http_errors.default.BadRequest,
  301. 'Received bytes do not match the "content-length" header.'
  302. );
  303. reject(error);
  304. return;
  305. }
  306. const buffer = Buffer.concat(data);
  307. const body = buffer.toString(encoding);
  308. resolve(body || void 0);
  309. }, "onEnd");
  310. const onError = /* @__PURE__ */ __name((error) => {
  311. request.removeAllListeners();
  312. reject((0, import_http_errors.default)(400, error));
  313. }, "onError");
  314. request.on("data", onData);
  315. request.on("end", onEnd);
  316. request.on("error", onError);
  317. request.resume();
  318. });
  319. }
  320. __name(fetchRequestBody, "fetchRequestBody");
  321. // src/utils/parse-cookie-string.js
  322. var import_js_format6 = require("@e22m4u/js-format");
  323. function parseCookieString(input) {
  324. if (typeof input !== "string") {
  325. throw new import_js_format6.InvalidArgumentError(
  326. 'The first parameter of "parseCookieString" must be a String, but %v was given.',
  327. input
  328. );
  329. }
  330. return input.split(";").filter((v) => v !== "").map((v) => v.split("=")).reduce((cookies, tuple) => {
  331. const key = decodeURIComponent(tuple[0]).trim();
  332. if (key === "__proto__" || key === "constructor" || key === "prototype") {
  333. return cookies;
  334. }
  335. const value = tuple[1] !== void 0 ? decodeURIComponent(tuple[1]).trim() : "";
  336. cookies[key] = value;
  337. return cookies;
  338. }, {});
  339. }
  340. __name(parseCookieString, "parseCookieString");
  341. // src/utils/create-request-mock.js
  342. var import_net = require("net");
  343. var import_tls = require("tls");
  344. var import_http2 = require("http");
  345. var import_querystring = __toESM(require("querystring"), 1);
  346. var import_js_format8 = require("@e22m4u/js-format");
  347. // src/utils/create-cookie-string.js
  348. var import_js_format7 = require("@e22m4u/js-format");
  349. function createCookieString(data) {
  350. if (!data || typeof data !== "object" || Array.isArray(data)) {
  351. throw new import_js_format7.InvalidArgumentError(
  352. 'The first parameter of "createCookieString" must be an Object, but %v was given.',
  353. data
  354. );
  355. }
  356. let cookies = "";
  357. for (const key in data) {
  358. if (!Object.prototype.hasOwnProperty.call(data, key)) {
  359. continue;
  360. }
  361. const val = data[key];
  362. if (val == null) {
  363. continue;
  364. }
  365. cookies += `${key}=${val}; `;
  366. }
  367. return cookies.trim();
  368. }
  369. __name(createCookieString, "createCookieString");
  370. // src/utils/create-request-mock.js
  371. function createRequestMock(patch) {
  372. if (patch != null && typeof patch !== "object" || Array.isArray(patch)) {
  373. throw new import_js_format8.InvalidArgumentError(
  374. 'The first parameter of "createRequestMock" must be an Object, but %v was given.',
  375. patch
  376. );
  377. }
  378. patch = patch || {};
  379. if (patch.host != null && typeof patch.host !== "string") {
  380. throw new import_js_format8.InvalidArgumentError(
  381. 'The parameter "host" of "createRequestMock" must be a String, but %v was given.',
  382. patch.host
  383. );
  384. }
  385. if (patch.method != null && typeof patch.method !== "string") {
  386. throw new import_js_format8.InvalidArgumentError(
  387. 'The parameter "method" of "createRequestMock" must be a String, but %v was given.',
  388. patch.method
  389. );
  390. }
  391. if (patch.secure != null && typeof patch.secure !== "boolean") {
  392. throw new import_js_format8.InvalidArgumentError(
  393. 'The parameter "secure" of "createRequestMock" must be a Boolean, but %v was given.',
  394. patch.secure
  395. );
  396. }
  397. if (patch.path != null && typeof patch.path !== "string") {
  398. throw new import_js_format8.InvalidArgumentError(
  399. 'The parameter "path" of "createRequestMock" must be a String, but %v was given.',
  400. patch.path
  401. );
  402. }
  403. if (patch.query != null && typeof patch.query !== "object" && typeof patch.query !== "string" || Array.isArray(patch.query)) {
  404. throw new import_js_format8.InvalidArgumentError(
  405. 'The parameter "query" of "createRequestMock" must be a String or Object, but %v was given.',
  406. patch.query
  407. );
  408. }
  409. if (patch.cookies != null && typeof patch.cookies !== "string" && typeof patch.cookies !== "object" || Array.isArray(patch.cookies)) {
  410. throw new import_js_format8.InvalidArgumentError(
  411. 'The parameter "cookies" of "createRequestMock" must be a String or Object, but %v was given.',
  412. patch.cookies
  413. );
  414. }
  415. if (patch.headers != null && typeof patch.headers !== "object" || Array.isArray(patch.headers)) {
  416. throw new import_js_format8.InvalidArgumentError(
  417. 'The parameter "headers" of "createRequestMock" must be an Object, but %v was given.',
  418. patch.headers
  419. );
  420. }
  421. if (patch.stream != null && !isReadableStream(patch.stream)) {
  422. throw new import_js_format8.InvalidArgumentError(
  423. 'The parameter "stream" of "createRequestMock" must be a Stream, but %v was given.',
  424. patch.stream
  425. );
  426. }
  427. if (patch.encoding != null) {
  428. if (typeof patch.encoding !== "string") {
  429. throw new import_js_format8.InvalidArgumentError(
  430. 'The parameter "encoding" of "createRequestMock" must be a String, but %v was given.',
  431. patch.encoding
  432. );
  433. }
  434. if (!CHARACTER_ENCODING_LIST.includes(patch.encoding)) {
  435. throw new import_js_format8.InvalidArgumentError(
  436. "Character encoding %v is not supported.",
  437. patch.encoding
  438. );
  439. }
  440. }
  441. if (patch.stream) {
  442. if (patch.secure != null) {
  443. throw new import_js_format8.InvalidArgumentError(
  444. 'The "createRequestMock" does not allow specifying the "stream" and "secure" options simultaneously.'
  445. );
  446. }
  447. if (patch.body != null) {
  448. throw new import_js_format8.InvalidArgumentError(
  449. 'The "createRequestMock" does not allow specifying the "stream" and "body" options simultaneously.'
  450. );
  451. }
  452. if (patch.encoding != null) {
  453. throw new import_js_format8.InvalidArgumentError(
  454. 'The "createRequestMock" does not allow specifying the "stream" and "encoding" options simultaneously.'
  455. );
  456. }
  457. }
  458. const request = patch.stream || createRequestStream(patch.secure, patch.body, patch.encoding);
  459. request.url = createRequestUrl(patch.path || "/", patch.query);
  460. request.headers = createRequestHeaders(
  461. patch.host,
  462. patch.secure,
  463. patch.body,
  464. patch.cookies,
  465. patch.encoding,
  466. patch.headers
  467. );
  468. request.method = (patch.method || "get").toUpperCase();
  469. return request;
  470. }
  471. __name(createRequestMock, "createRequestMock");
  472. function createRequestStream(secure, body, encoding) {
  473. if (encoding != null && typeof encoding !== "string") {
  474. throw new import_js_format8.InvalidArgumentError(
  475. 'The parameter "encoding" of "createRequestStream" must be a String, but %v was given.',
  476. encoding
  477. );
  478. }
  479. encoding = encoding || "utf-8";
  480. let socket = new import_net.Socket();
  481. if (secure) {
  482. socket = new import_tls.TLSSocket(socket);
  483. }
  484. const request = new import_http2.IncomingMessage(socket);
  485. if (body != null) {
  486. if (typeof body === "string") {
  487. request.push(body, encoding);
  488. } else if (Buffer.isBuffer(body)) {
  489. request.push(body);
  490. } else {
  491. request.push(JSON.stringify(body));
  492. }
  493. }
  494. request.push(null);
  495. return request;
  496. }
  497. __name(createRequestStream, "createRequestStream");
  498. function createRequestUrl(path, query) {
  499. if (typeof path !== "string") {
  500. throw new import_js_format8.InvalidArgumentError(
  501. 'The parameter "path" of "createRequestUrl" must be a String, but %v was given.',
  502. path
  503. );
  504. }
  505. if (query != null && typeof query !== "string" && typeof query !== "object" || Array.isArray(query)) {
  506. throw new import_js_format8.InvalidArgumentError(
  507. 'The parameter "query" of "createRequestUrl" must be a String or Object, but %v was given.',
  508. query
  509. );
  510. }
  511. let url = ("/" + path).replace("//", "/");
  512. if (typeof query === "object") {
  513. const qs = import_querystring.default.stringify(query);
  514. if (qs) {
  515. url += `?${qs}`;
  516. }
  517. } else if (typeof query === "string") {
  518. url += `?${query.replace(/^\?/, "")}`;
  519. }
  520. return url;
  521. }
  522. __name(createRequestUrl, "createRequestUrl");
  523. function createRequestHeaders(host, secure, body, cookies, encoding, headers) {
  524. if (host != null && typeof host !== "string") {
  525. throw new import_js_format8.InvalidArgumentError(
  526. 'The parameter "host" of "createRequestHeaders" a non-empty String, but %v was given.',
  527. host
  528. );
  529. }
  530. host = host || "localhost";
  531. if (secure != null && typeof secure !== "boolean") {
  532. throw new import_js_format8.InvalidArgumentError(
  533. 'The parameter "secure" of "createRequestHeaders" must be a String, but %v was given.',
  534. secure
  535. );
  536. }
  537. secure = Boolean(secure);
  538. if (cookies != null && typeof cookies !== "object" && typeof cookies !== "string" || Array.isArray(cookies)) {
  539. throw new import_js_format8.InvalidArgumentError(
  540. 'The parameter "cookies" of "createRequestHeaders" must be a String or Object, but %v was given.',
  541. cookies
  542. );
  543. }
  544. if (headers != null && typeof headers !== "object" || Array.isArray(headers)) {
  545. throw new import_js_format8.InvalidArgumentError(
  546. 'The parameter "headers" of "createRequestHeaders" must be an Object, but %v was given.',
  547. headers
  548. );
  549. }
  550. headers = headers || {};
  551. if (encoding != null && typeof encoding !== "string") {
  552. throw new import_js_format8.InvalidArgumentError(
  553. 'The parameter "encoding" of "createRequestHeaders" must be a String, but %v was given.',
  554. encoding
  555. );
  556. }
  557. encoding = encoding || "utf-8";
  558. const obj = { ...headers };
  559. obj["host"] = host;
  560. if (secure) {
  561. obj["x-forwarded-proto"] = "https";
  562. }
  563. if (cookies != null) {
  564. if (typeof cookies === "string") {
  565. obj["cookie"] = obj["cookie"] ? obj["cookie"] : "";
  566. obj["cookie"] += obj["cookie"] ? `; ${cookies}` : cookies;
  567. } else if (typeof cookies === "object") {
  568. obj["cookie"] = obj["cookie"] ? obj["cookie"] : "";
  569. const newCookies = createCookieString(cookies);
  570. obj["cookie"] += obj["cookie"] ? `; ${newCookies}` : newCookies;
  571. }
  572. }
  573. if (obj["content-type"] == null) {
  574. if (typeof body === "string") {
  575. obj["content-type"] = "text/plain";
  576. } else if (Buffer.isBuffer(body)) {
  577. obj["content-type"] = "application/octet-stream";
  578. } else if (typeof body === "object" || typeof body === "boolean" || typeof body === "number") {
  579. obj["content-type"] = "application/json";
  580. }
  581. }
  582. if (body != null && obj["content-length"] == null) {
  583. if (typeof body === "string") {
  584. const length = Buffer.byteLength(body, encoding);
  585. obj["content-length"] = String(length);
  586. } else if (Buffer.isBuffer(body)) {
  587. const length = Buffer.byteLength(body);
  588. obj["content-length"] = String(length);
  589. } else if (typeof body === "object" || typeof body === "boolean" || typeof body === "number") {
  590. const json = JSON.stringify(body);
  591. const length = Buffer.byteLength(json, encoding);
  592. obj["content-length"] = String(length);
  593. }
  594. }
  595. return obj;
  596. }
  597. __name(createRequestHeaders, "createRequestHeaders");
  598. // src/utils/create-response-mock.js
  599. var import_stream = require("stream");
  600. function createResponseMock() {
  601. const response = new import_stream.PassThrough();
  602. patchEncoding(response);
  603. patchHeaders(response);
  604. patchBody(response);
  605. return response;
  606. }
  607. __name(createResponseMock, "createResponseMock");
  608. function patchEncoding(response) {
  609. Object.defineProperty(response, "_encoding", {
  610. configurable: true,
  611. writable: true,
  612. value: void 0
  613. });
  614. Object.defineProperty(response, "setEncoding", {
  615. configurable: true,
  616. value: /* @__PURE__ */ __name(function(enc) {
  617. this._encoding = enc;
  618. return this;
  619. }, "value")
  620. });
  621. Object.defineProperty(response, "getEncoding", {
  622. configurable: true,
  623. value: /* @__PURE__ */ __name(function() {
  624. return this._encoding;
  625. }, "value")
  626. });
  627. }
  628. __name(patchEncoding, "patchEncoding");
  629. function patchHeaders(response) {
  630. Object.defineProperty(response, "_headersSent", {
  631. configurable: true,
  632. writable: true,
  633. value: false
  634. });
  635. Object.defineProperty(response, "headersSent", {
  636. configurable: true,
  637. get() {
  638. return this._headersSent;
  639. }
  640. });
  641. Object.defineProperty(response, "_headers", {
  642. configurable: true,
  643. writable: true,
  644. value: {}
  645. });
  646. Object.defineProperty(response, "setHeader", {
  647. configurable: true,
  648. value: /* @__PURE__ */ __name(function(name, value) {
  649. if (this.headersSent) {
  650. throw new Error(
  651. "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client"
  652. );
  653. }
  654. const key = name.toLowerCase();
  655. this._headers[key] = String(value);
  656. return this;
  657. }, "value")
  658. });
  659. Object.defineProperty(response, "getHeader", {
  660. configurable: true,
  661. value: /* @__PURE__ */ __name(function(name) {
  662. return this._headers[name.toLowerCase()];
  663. }, "value")
  664. });
  665. Object.defineProperty(response, "getHeaders", {
  666. configurable: true,
  667. value: /* @__PURE__ */ __name(function() {
  668. return JSON.parse(JSON.stringify(this._headers));
  669. }, "value")
  670. });
  671. }
  672. __name(patchHeaders, "patchHeaders");
  673. function patchBody(response) {
  674. let resolve, reject;
  675. const promise = new Promise((rsv, rej) => {
  676. resolve = rsv;
  677. reject = rej;
  678. });
  679. const data = [];
  680. response.on("data", (c) => data.push(c));
  681. response.on("error", (e) => reject(e));
  682. response.on("end", () => {
  683. resolve(Buffer.concat(data));
  684. });
  685. const originalEnd = response.end.bind(response);
  686. response.end = function(...args) {
  687. this._headersSent = true;
  688. return originalEnd(...args);
  689. };
  690. Object.defineProperty(response, "getBody", {
  691. configurable: true,
  692. value: /* @__PURE__ */ __name(function() {
  693. return promise.then((buffer) => {
  694. const enc = this.getEncoding();
  695. const str = buffer.toString(enc);
  696. return data.length ? str : void 0;
  697. });
  698. }, "value")
  699. });
  700. }
  701. __name(patchBody, "patchBody");
  702. // src/utils/get-request-pathname.js
  703. var import_js_format9 = require("@e22m4u/js-format");
  704. function getRequestPathname(request) {
  705. if (!request || typeof request !== "object" || Array.isArray(request) || typeof request.url !== "string") {
  706. throw new import_js_format9.InvalidArgumentError(
  707. 'The first parameter of "getRequestPathname" must be an instance of IncomingMessage, but %v was given.',
  708. request
  709. );
  710. }
  711. return (request.url || "/").replace(/\?.*$/, "");
  712. }
  713. __name(getRequestPathname, "getRequestPathname");
  714. // src/hooks/hook-registry.js
  715. var import_js_format10 = require("@e22m4u/js-format");
  716. var RouterHookType = {
  717. PRE_HANDLER: "preHandler",
  718. POST_HANDLER: "postHandler"
  719. };
  720. var _HookRegistry = class _HookRegistry {
  721. /**
  722. * Hooks.
  723. *
  724. * @type {Map<string, Function[]>}
  725. * @private
  726. */
  727. _hooks = /* @__PURE__ */ new Map();
  728. /**
  729. * Add hook.
  730. *
  731. * @param {string} type
  732. * @param {Function} hook
  733. * @returns {this}
  734. */
  735. addHook(type, hook) {
  736. if (!type || typeof type !== "string") {
  737. throw new import_js_format10.InvalidArgumentError(
  738. "The hook type is required, but %v was given.",
  739. type
  740. );
  741. }
  742. if (!Object.values(RouterHookType).includes(type)) {
  743. throw new import_js_format10.InvalidArgumentError(
  744. "The hook type %v is not supported.",
  745. type
  746. );
  747. }
  748. if (!hook || typeof hook !== "function") {
  749. throw new import_js_format10.InvalidArgumentError(
  750. "The hook %v must be a Function, but %v was given.",
  751. type,
  752. hook
  753. );
  754. }
  755. const hooks = this._hooks.get(type) || [];
  756. hooks.push(hook);
  757. this._hooks.set(type, hooks);
  758. return this;
  759. }
  760. /**
  761. * Has hook.
  762. *
  763. * @param {string} type
  764. * @param {Function} hook
  765. * @returns {boolean}
  766. */
  767. hasHook(type, hook) {
  768. if (!type || typeof type !== "string") {
  769. throw new import_js_format10.InvalidArgumentError(
  770. "The hook type is required, but %v was given.",
  771. type
  772. );
  773. }
  774. if (!Object.values(RouterHookType).includes(type)) {
  775. throw new import_js_format10.InvalidArgumentError(
  776. "The hook type %v is not supported.",
  777. type
  778. );
  779. }
  780. if (!hook || typeof hook !== "function") {
  781. throw new import_js_format10.InvalidArgumentError(
  782. "The hook %v must be a Function, but %v was given.",
  783. type,
  784. hook
  785. );
  786. }
  787. const hooks = this._hooks.get(type) || [];
  788. return hooks.indexOf(hook) > -1;
  789. }
  790. /**
  791. * Get hooks.
  792. *
  793. * @param {string} type
  794. * @returns {Function[]}
  795. */
  796. getHooks(type) {
  797. if (!type || typeof type !== "string") {
  798. throw new import_js_format10.InvalidArgumentError(
  799. "The hook type is required, but %v was given.",
  800. type
  801. );
  802. }
  803. if (!Object.values(RouterHookType).includes(type)) {
  804. throw new import_js_format10.InvalidArgumentError(
  805. "The hook type %v is not supported.",
  806. type
  807. );
  808. }
  809. return this._hooks.get(type) || [];
  810. }
  811. };
  812. __name(_HookRegistry, "HookRegistry");
  813. var HookRegistry = _HookRegistry;
  814. // src/hooks/hook-invoker.js
  815. var _HookInvoker = class _HookInvoker extends DebuggableService {
  816. /**
  817. * Invoke and continue until value received.
  818. *
  819. * @param {Route} route
  820. * @param {string} hookType
  821. * @param {import('http').ServerResponse} response
  822. * @param {*[]} args
  823. * @returns {Promise<*>|*}
  824. */
  825. invokeAndContinueUntilValueReceived(route, hookType, response, ...args) {
  826. if (!route || !(route instanceof Route)) {
  827. throw new import_js_format11.InvalidArgumentError(
  828. 'The parameter "route" of the HookInvoker.invokeAndContinueUntilValueReceived must be a Route instance, but %v was given.',
  829. route
  830. );
  831. }
  832. if (!hookType || typeof hookType !== "string") {
  833. throw new import_js_format11.InvalidArgumentError(
  834. 'The parameter "hookType" of the HookInvoker.invokeAndContinueUntilValueReceived must be a non-empty String, but %v was given.',
  835. hookType
  836. );
  837. }
  838. if (!Object.values(RouterHookType).includes(hookType)) {
  839. throw new import_js_format11.InvalidArgumentError(
  840. "The hook type %v is not supported.",
  841. hookType
  842. );
  843. }
  844. if (!response || typeof response !== "object" || Array.isArray(response) || typeof response.headersSent !== "boolean") {
  845. throw new import_js_format11.InvalidArgumentError(
  846. 'The parameter "response" of the HookInvoker.invokeAndContinueUntilValueReceived must be a ServerResponse instance, but %v was given.',
  847. response
  848. );
  849. }
  850. if (isResponseSent(response)) {
  851. return response;
  852. }
  853. const hooks = [
  854. ...this.getService(HookRegistry).getHooks(hookType),
  855. ...route.hookRegistry.getHooks(hookType)
  856. ];
  857. let result = void 0;
  858. for (let i = 0; i < hooks.length; i++) {
  859. const hook = hooks[i];
  860. result = hook(...args);
  861. if (isResponseSent(response)) {
  862. return response;
  863. }
  864. if (result != null) {
  865. if (isPromise(result)) {
  866. return (async () => {
  867. let asyncResult = await result;
  868. if (isResponseSent(response)) {
  869. return response;
  870. }
  871. if (asyncResult != null) {
  872. return asyncResult;
  873. }
  874. for (let j = i + 1; j < hooks.length; j++) {
  875. asyncResult = await hooks[j](...args);
  876. if (isResponseSent(response)) {
  877. return response;
  878. }
  879. if (asyncResult != null) {
  880. return asyncResult;
  881. }
  882. }
  883. return;
  884. })();
  885. }
  886. return result;
  887. }
  888. }
  889. return;
  890. }
  891. };
  892. __name(_HookInvoker, "HookInvoker");
  893. var HookInvoker = _HookInvoker;
  894. // src/route/validate-route-definition.js
  895. var import_js_format12 = require("@e22m4u/js-format");
  896. function validateRouteDefinition(routeDef) {
  897. if (!routeDef || typeof routeDef !== "object" || Array.isArray(routeDef)) {
  898. throw new import_js_format12.InvalidArgumentError(
  899. "Route definition must be an Object, but %v was given.",
  900. routeDef
  901. );
  902. }
  903. if (!routeDef.method || typeof routeDef.method !== "string") {
  904. throw new import_js_format12.InvalidArgumentError(
  905. 'Option "method" must be a non-empty String, but %v was given.',
  906. routeDef.method
  907. );
  908. }
  909. if (!routeDef.path || typeof routeDef.path !== "string") {
  910. throw new import_js_format12.InvalidArgumentError(
  911. 'Option "path" must be a non-empty String, but %v was given.',
  912. routeDef.path
  913. );
  914. }
  915. if (typeof routeDef.handler !== "function") {
  916. throw new import_js_format12.InvalidArgumentError(
  917. 'Option "handler" must be a Function, but %v was given.',
  918. routeDef.handler
  919. );
  920. }
  921. if (routeDef.preHandler !== void 0) {
  922. if (Array.isArray(routeDef.preHandler)) {
  923. routeDef.preHandler.forEach((preHandler) => {
  924. if (typeof preHandler !== "function") {
  925. throw new import_js_format12.InvalidArgumentError(
  926. "Route pre-handler must be a Function, but %v was given.",
  927. preHandler
  928. );
  929. }
  930. });
  931. } else if (typeof routeDef.preHandler !== "function") {
  932. throw new import_js_format12.InvalidArgumentError(
  933. 'Option "preHandler" must be a Function or an Array, but %v was given.',
  934. routeDef.preHandler
  935. );
  936. }
  937. }
  938. if (routeDef.postHandler !== void 0) {
  939. if (Array.isArray(routeDef.postHandler)) {
  940. routeDef.postHandler.forEach((postHandler) => {
  941. if (typeof postHandler !== "function") {
  942. throw new import_js_format12.InvalidArgumentError(
  943. "Route post-handler must be a Function, but %v was given.",
  944. postHandler
  945. );
  946. }
  947. });
  948. } else if (typeof routeDef.postHandler !== "function") {
  949. throw new import_js_format12.InvalidArgumentError(
  950. 'Option "postHandler" must be a Function or an Array, but %v was given.',
  951. routeDef.postHandler
  952. );
  953. }
  954. }
  955. if (routeDef.meta !== void 0) {
  956. if (!routeDef.meta || typeof routeDef.meta !== "object" || Array.isArray(routeDef.meta)) {
  957. throw new import_js_format12.InvalidArgumentError(
  958. 'Option "meta" must be an Object, but %v was given.',
  959. routeDef.meta
  960. );
  961. }
  962. }
  963. }
  964. __name(validateRouteDefinition, "validateRouteDefinition");
  965. // src/route/route.js
  966. var HttpMethod = {
  967. GET: "GET",
  968. POST: "POST",
  969. PUT: "PUT",
  970. PATCH: "PATCH",
  971. DELETE: "DELETE"
  972. };
  973. var ROOT_PATH = "/";
  974. var _Route = class _Route extends import_js_debug.Debuggable {
  975. /**
  976. * Method.
  977. *
  978. * @type {string}
  979. * @private
  980. */
  981. _method;
  982. /**
  983. * Getter of the method.
  984. *
  985. * @returns {string}
  986. */
  987. get method() {
  988. return this._method;
  989. }
  990. /**
  991. * Path template.
  992. *
  993. * @type {string}
  994. * @private
  995. */
  996. _path;
  997. /**
  998. * Getter of the path.
  999. *
  1000. * @returns {string}
  1001. */
  1002. get path() {
  1003. return this._path;
  1004. }
  1005. /**
  1006. * Meta.
  1007. *
  1008. * @type {object}
  1009. */
  1010. _meta = {};
  1011. /**
  1012. * Getter of the meta.
  1013. *
  1014. * @returns {object}
  1015. */
  1016. get meta() {
  1017. return this._meta;
  1018. }
  1019. /**
  1020. * Handler.
  1021. *
  1022. * @type {RouteHandler}
  1023. * @private
  1024. */
  1025. _handler;
  1026. /**
  1027. * Getter of the handler.
  1028. *
  1029. * @returns {*}
  1030. */
  1031. get handler() {
  1032. return this._handler;
  1033. }
  1034. /**
  1035. * Hook registry.
  1036. *
  1037. * @type {HookRegistry}
  1038. * @private
  1039. */
  1040. _hookRegistry = new HookRegistry();
  1041. /**
  1042. * Getter of the hook registry.
  1043. *
  1044. * @returns {HookRegistry}
  1045. */
  1046. get hookRegistry() {
  1047. return this._hookRegistry;
  1048. }
  1049. /**
  1050. * Constructor.
  1051. *
  1052. * @param {RouteDefinition} routeDef
  1053. */
  1054. constructor(routeDef) {
  1055. super({
  1056. namespace: MODULE_DEBUG_NAMESPACE,
  1057. noEnvironmentNamespace: true,
  1058. noInstantiationMessage: true
  1059. });
  1060. validateRouteDefinition(routeDef);
  1061. this._method = routeDef.method.toUpperCase();
  1062. this._path = routeDef.path;
  1063. if (routeDef.meta !== void 0) {
  1064. this._meta = cloneDeep(routeDef.meta);
  1065. }
  1066. this._handler = routeDef.handler;
  1067. if (routeDef.preHandler !== void 0) {
  1068. const preHandlerHooks = Array.isArray(routeDef.preHandler) ? routeDef.preHandler : [routeDef.preHandler];
  1069. preHandlerHooks.forEach((hook) => {
  1070. this._hookRegistry.addHook(RouterHookType.PRE_HANDLER, hook);
  1071. });
  1072. }
  1073. if (routeDef.postHandler !== void 0) {
  1074. const postHandlerHooks = Array.isArray(routeDef.postHandler) ? routeDef.postHandler : [routeDef.postHandler];
  1075. postHandlerHooks.forEach((hook) => {
  1076. this._hookRegistry.addHook(RouterHookType.POST_HANDLER, hook);
  1077. });
  1078. }
  1079. this.ctorDebug("A new route %s %v was created.", this._method, this._path);
  1080. }
  1081. /**
  1082. * Handle request.
  1083. *
  1084. * @param {RequestContext} context
  1085. * @returns {*}
  1086. */
  1087. handle(context) {
  1088. const debug = this.getDebuggerFor(this.handle);
  1089. const requestPath = getRequestPathname(context.request);
  1090. debug(
  1091. "Invoking the Route handler for the request %s %v.",
  1092. this.method.toUpperCase(),
  1093. requestPath
  1094. );
  1095. return this._handler(context);
  1096. }
  1097. };
  1098. __name(_Route, "Route");
  1099. var Route = _Route;
  1100. // src/parsers/body-parser.js
  1101. var import_http_errors2 = __toESM(require("http-errors"), 1);
  1102. // src/router-options.js
  1103. var import_js_format13 = require("@e22m4u/js-format");
  1104. var _RouterOptions = class _RouterOptions extends DebuggableService {
  1105. /**
  1106. * Request body bytes limit.
  1107. *
  1108. * @type {number}
  1109. * @private
  1110. */
  1111. _requestBodyBytesLimit = 512e3;
  1112. // 512kb
  1113. /**
  1114. * Getter of request body bytes limit.
  1115. *
  1116. * @returns {number}
  1117. */
  1118. get requestBodyBytesLimit() {
  1119. return this._requestBodyBytesLimit;
  1120. }
  1121. /**
  1122. * Set request body bytes limit.
  1123. *
  1124. * @param {number} input
  1125. * @returns {RouterOptions}
  1126. */
  1127. setRequestBodyBytesLimit(input) {
  1128. if (typeof input !== "number" || input < 0) {
  1129. throw new import_js_format13.InvalidArgumentError(
  1130. 'The option "requestBodyBytesLimit" must be a positive Number or 0, but %v was given.',
  1131. input
  1132. );
  1133. }
  1134. this._requestBodyBytesLimit = input;
  1135. return this;
  1136. }
  1137. };
  1138. __name(_RouterOptions, "RouterOptions");
  1139. var RouterOptions = _RouterOptions;
  1140. // src/parsers/body-parser.js
  1141. var import_js_format14 = require("@e22m4u/js-format");
  1142. var METHODS_WITH_BODY = ["POST", "PUT", "PATCH", "DELETE"];
  1143. var UNPARSABLE_MEDIA_TYPES = ["multipart/form-data"];
  1144. var _BodyParser = class _BodyParser extends DebuggableService {
  1145. /**
  1146. * Parsers.
  1147. *
  1148. * @type {{[mime: string]: Function}}
  1149. */
  1150. _parsers = {
  1151. "text/plain": /* @__PURE__ */ __name((v) => String(v), "text/plain"),
  1152. "application/json": parseJsonBody
  1153. };
  1154. /**
  1155. * Set parser.
  1156. *
  1157. * @param {string} mediaType
  1158. * @param {Function} parser
  1159. * @returns {this}
  1160. */
  1161. defineParser(mediaType, parser) {
  1162. if (!mediaType || typeof mediaType !== "string") {
  1163. throw new import_js_format14.InvalidArgumentError(
  1164. 'The parameter "mediaType" of BodyParser.defineParser must be a non-empty String, but %v was given.',
  1165. mediaType
  1166. );
  1167. }
  1168. if (!parser || typeof parser !== "function") {
  1169. throw new import_js_format14.InvalidArgumentError(
  1170. 'The parameter "parser" of BodyParser.defineParser must be a Function, but %v was given.',
  1171. parser
  1172. );
  1173. }
  1174. this._parsers[mediaType] = parser;
  1175. return this;
  1176. }
  1177. /**
  1178. * Has parser.
  1179. *
  1180. * @param {string} mediaType
  1181. * @returns {boolean}
  1182. */
  1183. hasParser(mediaType) {
  1184. if (!mediaType || typeof mediaType !== "string") {
  1185. throw new import_js_format14.InvalidArgumentError(
  1186. 'The parameter "mediaType" of BodyParser.hasParser must be a non-empty String, but %v was given.',
  1187. mediaType
  1188. );
  1189. }
  1190. return Boolean(this._parsers[mediaType]);
  1191. }
  1192. /**
  1193. * Delete parser.
  1194. *
  1195. * @param {string} mediaType
  1196. * @returns {this}
  1197. */
  1198. deleteParser(mediaType) {
  1199. if (!mediaType || typeof mediaType !== "string") {
  1200. throw new import_js_format14.InvalidArgumentError(
  1201. 'The parameter "mediaType" of BodyParser.deleteParser must be a non-empty String, but %v was given.',
  1202. mediaType
  1203. );
  1204. }
  1205. const parser = this._parsers[mediaType];
  1206. if (!parser) {
  1207. throw new import_js_format14.InvalidArgumentError(
  1208. "The parser of %v is not found.",
  1209. mediaType
  1210. );
  1211. }
  1212. delete this._parsers[mediaType];
  1213. return this;
  1214. }
  1215. /**
  1216. * Parse.
  1217. *
  1218. * @param {import('http').IncomingMessage} request
  1219. * @returns {Promise<*>|undefined}
  1220. */
  1221. parse(request) {
  1222. const debug = this.getDebuggerFor(this.parse);
  1223. if (!METHODS_WITH_BODY.includes(request.method.toUpperCase())) {
  1224. debug(
  1225. "Body parsing was skipped for the %s request.",
  1226. request.method.toUpperCase()
  1227. );
  1228. return;
  1229. }
  1230. const contentType = (request.headers["content-type"] || "").replace(
  1231. /^([^;]+);.*$/,
  1232. "$1"
  1233. );
  1234. if (!contentType) {
  1235. debug(
  1236. "Body parsing was skipped because the request had no content type."
  1237. );
  1238. return;
  1239. }
  1240. const { mediaType } = parseContentType(contentType);
  1241. if (!mediaType) {
  1242. throw createError(
  1243. import_http_errors2.default.BadRequest,
  1244. 'Unable to parse the "content-type" header.'
  1245. );
  1246. }
  1247. const parser = this._parsers[mediaType];
  1248. if (!parser) {
  1249. if (UNPARSABLE_MEDIA_TYPES.includes(mediaType)) {
  1250. debug("Body parsing was skipped for %v.", mediaType);
  1251. return;
  1252. }
  1253. throw createError(
  1254. import_http_errors2.default.UnsupportedMediaType,
  1255. "Media type %v is not supported.",
  1256. mediaType
  1257. );
  1258. }
  1259. const bodyBytesLimit = this.getService(RouterOptions).requestBodyBytesLimit;
  1260. return fetchRequestBody(request, bodyBytesLimit).then((rawBody) => {
  1261. if (rawBody != null) {
  1262. return parser(rawBody);
  1263. }
  1264. return rawBody;
  1265. });
  1266. }
  1267. };
  1268. __name(_BodyParser, "BodyParser");
  1269. var BodyParser = _BodyParser;
  1270. function parseJsonBody(input) {
  1271. if (typeof input !== "string") {
  1272. return void 0;
  1273. }
  1274. try {
  1275. return JSON.parse(input);
  1276. } catch (error) {
  1277. throw createError(import_http_errors2.default.BadRequest, error.message);
  1278. }
  1279. }
  1280. __name(parseJsonBody, "parseJsonBody");
  1281. // src/parsers/query-parser.js
  1282. var import_querystring2 = __toESM(require("querystring"), 1);
  1283. var _QueryParser = class _QueryParser extends DebuggableService {
  1284. /**
  1285. * Parse
  1286. *
  1287. * @param {import('http').IncomingMessage} request
  1288. * @returns {object}
  1289. */
  1290. parse(request) {
  1291. const debug = this.getDebuggerFor(this.parse);
  1292. const queryStr = request.url.replace(/^[^?]*\??/, "");
  1293. const query = queryStr ? import_querystring2.default.parse(queryStr) : {};
  1294. const queryKeys = Object.keys(query);
  1295. if (queryKeys.length) {
  1296. queryKeys.forEach((key) => {
  1297. debug("The query parameter %v had the value %v.", key, query[key]);
  1298. });
  1299. } else {
  1300. debug(
  1301. "The request %s %v had no query parameters.",
  1302. request.method,
  1303. getRequestPathname(request)
  1304. );
  1305. }
  1306. return query;
  1307. }
  1308. };
  1309. __name(_QueryParser, "QueryParser");
  1310. var QueryParser = _QueryParser;
  1311. // src/parsers/cookies-parser.js
  1312. var _CookiesParser = class _CookiesParser extends DebuggableService {
  1313. /**
  1314. * Parse
  1315. *
  1316. * @param {import('http').IncomingMessage} request
  1317. * @returns {object}
  1318. */
  1319. parse(request) {
  1320. const debug = this.getDebuggerFor(this.parse);
  1321. const cookiesString = request.headers["cookie"] || "";
  1322. const cookies = parseCookieString(cookiesString);
  1323. const cookiesKeys = Object.keys(cookies);
  1324. if (cookiesKeys.length) {
  1325. cookiesKeys.forEach((key) => {
  1326. debug("The cookie %v had the value %v.", key, cookies[key]);
  1327. });
  1328. } else {
  1329. debug(
  1330. "The request %s %v had no cookies.",
  1331. request.method,
  1332. getRequestPathname(request)
  1333. );
  1334. }
  1335. return cookies;
  1336. }
  1337. };
  1338. __name(_CookiesParser, "CookiesParser");
  1339. var CookiesParser = _CookiesParser;
  1340. // src/parsers/request-parser.js
  1341. var import_http3 = require("http");
  1342. var import_js_format15 = require("@e22m4u/js-format");
  1343. var _RequestParser = class _RequestParser extends DebuggableService {
  1344. /**
  1345. * Parse.
  1346. *
  1347. * @param {IncomingMessage} request
  1348. * @returns {Promise<object>|object}
  1349. */
  1350. parse(request) {
  1351. if (!(request instanceof import_http3.IncomingMessage)) {
  1352. throw new import_js_format15.InvalidArgumentError(
  1353. "The first parameter of RequestParser.parse must be an instance of IncomingMessage, but %v was given.",
  1354. request
  1355. );
  1356. }
  1357. const data = {};
  1358. const promises = [];
  1359. const parsedQuery = this.getService(QueryParser).parse(request);
  1360. if (isPromise(parsedQuery)) {
  1361. promises.push(parsedQuery.then((v) => data.query = v));
  1362. } else {
  1363. data.query = parsedQuery;
  1364. }
  1365. const parsedCookies = this.getService(CookiesParser).parse(request);
  1366. if (isPromise(parsedCookies)) {
  1367. promises.push(parsedCookies.then((v) => data.cookies = v));
  1368. } else {
  1369. data.cookies = parsedCookies;
  1370. }
  1371. const parsedBody = this.getService(BodyParser).parse(request);
  1372. if (isPromise(parsedBody)) {
  1373. promises.push(parsedBody.then((v) => data.body = v));
  1374. } else {
  1375. data.body = parsedBody;
  1376. }
  1377. data.headers = Object.assign({}, request.headers);
  1378. return promises.length ? Promise.all(promises).then(() => data) : data;
  1379. }
  1380. };
  1381. __name(_RequestParser, "RequestParser");
  1382. var RequestParser = _RequestParser;
  1383. // src/route-registry.js
  1384. var import_js_path_trie = require("@e22m4u/js-path-trie");
  1385. var import_js_service2 = require("@e22m4u/js-service");
  1386. var import_js_format16 = require("@e22m4u/js-format");
  1387. var _RouteRegistry = class _RouteRegistry extends DebuggableService {
  1388. /**
  1389. * Constructor.
  1390. *
  1391. * @param {ServiceContainer} container
  1392. */
  1393. constructor(container) {
  1394. super(container);
  1395. this._trie = new import_js_path_trie.PathTrie();
  1396. }
  1397. /**
  1398. * Define route.
  1399. *
  1400. * @param {import('./route/index.js').RouteDefinition} routeDef
  1401. * @returns {Route}
  1402. */
  1403. defineRoute(routeDef) {
  1404. const debug = this.getDebuggerFor(this.defineRoute);
  1405. if (!routeDef || typeof routeDef !== "object" || Array.isArray(routeDef)) {
  1406. throw new import_js_format16.InvalidArgumentError(
  1407. "The route definition must be an Object, but %v was given.",
  1408. routeDef
  1409. );
  1410. }
  1411. const route = new Route(routeDef);
  1412. const triePath = `${route.method}/${route.path}`;
  1413. this._trie.add(triePath, route);
  1414. debug(
  1415. "The route %s %v was registered.",
  1416. route.method.toUpperCase(),
  1417. route.path
  1418. );
  1419. return route;
  1420. }
  1421. /**
  1422. * Match route by request.
  1423. *
  1424. * @param {import('http').IncomingRequest} request
  1425. * @returns {ResolvedRoute|undefined}
  1426. */
  1427. matchRouteByRequest(request) {
  1428. const debug = this.getDebuggerFor(this.matchRouteByRequest);
  1429. const requestPath = (request.url || "/").replace(/\?.*$/, "");
  1430. debug(
  1431. "Matching routes with the request %s %v.",
  1432. request.method.toUpperCase(),
  1433. requestPath
  1434. );
  1435. const rawTriePath = `${request.method.toUpperCase()}/${requestPath}`;
  1436. const triePath = rawTriePath.replace(/\/+/g, "/");
  1437. const resolved = this._trie.match(triePath);
  1438. if (resolved) {
  1439. const route = resolved.value;
  1440. debug(
  1441. "The route %s %v was matched.",
  1442. route.method.toUpperCase(),
  1443. route.path
  1444. );
  1445. const paramNames = Object.keys(resolved.params);
  1446. if (paramNames.length) {
  1447. paramNames.forEach((name) => {
  1448. debug(
  1449. "The path parameter %v had the value %v.",
  1450. name,
  1451. resolved.params[name]
  1452. );
  1453. });
  1454. } else {
  1455. debug("No path parameters found.");
  1456. }
  1457. return { route, params: resolved.params };
  1458. }
  1459. debug(
  1460. "No matched route for the request %s %v.",
  1461. request.method.toUpperCase(),
  1462. requestPath
  1463. );
  1464. }
  1465. };
  1466. __name(_RouteRegistry, "RouteRegistry");
  1467. var RouteRegistry = _RouteRegistry;
  1468. // src/request-context.js
  1469. var import_js_format17 = require("@e22m4u/js-format");
  1470. var import_js_service3 = require("@e22m4u/js-service");
  1471. var _RequestContext = class _RequestContext {
  1472. /**
  1473. * Service container.
  1474. *
  1475. * @type {ServiceContainer}
  1476. */
  1477. _container;
  1478. /**
  1479. * Getter of service container.
  1480. *
  1481. * @type {ServiceContainer}
  1482. */
  1483. get container() {
  1484. return this._container;
  1485. }
  1486. /**
  1487. * Request.
  1488. *
  1489. * @type {import('http').IncomingMessage}
  1490. */
  1491. _request;
  1492. /**
  1493. * Getter of request.
  1494. *
  1495. * @type {import('http').IncomingMessage}
  1496. */
  1497. get request() {
  1498. return this._request;
  1499. }
  1500. /**
  1501. * Response.
  1502. *
  1503. * @type {import('http').ServerResponse}
  1504. */
  1505. _response;
  1506. /**
  1507. * Getter of response.
  1508. *
  1509. * @type {import('http').ServerResponse}
  1510. */
  1511. get response() {
  1512. return this._response;
  1513. }
  1514. /**
  1515. * Route
  1516. *
  1517. * @type {Route}
  1518. */
  1519. _route;
  1520. /**
  1521. * Getter of route.
  1522. *
  1523. * @type {Route}
  1524. */
  1525. get route() {
  1526. return this._route;
  1527. }
  1528. /**
  1529. * Query.
  1530. *
  1531. * @type {object}
  1532. */
  1533. query = {};
  1534. /**
  1535. * Path parameters.
  1536. *
  1537. * @type {object}
  1538. */
  1539. params = {};
  1540. /**
  1541. * Headers.
  1542. *
  1543. * @type {object}
  1544. */
  1545. headers = {};
  1546. /**
  1547. * Parsed cookies.
  1548. *
  1549. * @type {object}
  1550. */
  1551. cookies = {};
  1552. /**
  1553. * Parsed body.
  1554. *
  1555. * @type {*}
  1556. */
  1557. body;
  1558. /**
  1559. * State.
  1560. *
  1561. * @type {object}
  1562. */
  1563. state = {};
  1564. /**
  1565. * Route meta.
  1566. *
  1567. * @type {import('./route/index.js').RouteMeta}
  1568. */
  1569. get meta() {
  1570. return this.route.meta;
  1571. }
  1572. /**
  1573. * Method.
  1574. *
  1575. * @returns {string}
  1576. */
  1577. get method() {
  1578. return this.request.method.toUpperCase();
  1579. }
  1580. /**
  1581. * Path.
  1582. *
  1583. * @returns {string}
  1584. */
  1585. get path() {
  1586. return this.request.url;
  1587. }
  1588. /**
  1589. * Pathname.
  1590. *
  1591. * @type {string|undefined}
  1592. * @private
  1593. */
  1594. _pathname = void 0;
  1595. /**
  1596. * Pathname.
  1597. *
  1598. * @returns {string}
  1599. */
  1600. get pathname() {
  1601. if (this._pathname != null) {
  1602. return this._pathname;
  1603. }
  1604. this._pathname = getRequestPathname(this.request);
  1605. return this._pathname;
  1606. }
  1607. /**
  1608. * Constructor.
  1609. *
  1610. * @param {ServiceContainer} container
  1611. * @param {import('http').IncomingMessage} request
  1612. * @param {import('http').ServerResponse} response
  1613. * @param {Route} route
  1614. */
  1615. constructor(container, request, response, route) {
  1616. if (!(0, import_js_service3.isServiceContainer)(container)) {
  1617. throw new import_js_format17.InvalidArgumentError(
  1618. 'The parameter "container" of RequestContext.constructor must be an instance of ServiceContainer, but %v was given.',
  1619. container
  1620. );
  1621. }
  1622. this._container = container;
  1623. if (!request || typeof request !== "object" || Array.isArray(request) || !isReadableStream(request)) {
  1624. throw new import_js_format17.InvalidArgumentError(
  1625. 'The parameter "request" of RequestContext.constructor must be an instance of IncomingMessage, but %v was given.',
  1626. request
  1627. );
  1628. }
  1629. this._request = request;
  1630. if (!response || typeof response !== "object" || Array.isArray(response) || !isWritableStream(response)) {
  1631. throw new import_js_format17.InvalidArgumentError(
  1632. 'The parameter "response" of RequestContext.constructor must be an instance of ServerResponse, but %v was given.',
  1633. response
  1634. );
  1635. }
  1636. this._response = response;
  1637. if (!(route instanceof Route)) {
  1638. throw new import_js_format17.InvalidArgumentError(
  1639. 'The parameter "route" of RequestContext.constructor must be an instance of Route, but %v was given.',
  1640. route
  1641. );
  1642. }
  1643. this._route = route;
  1644. }
  1645. };
  1646. __name(_RequestContext, "RequestContext");
  1647. var RequestContext = _RequestContext;
  1648. // src/trie-router.js
  1649. var import_js_service4 = require("@e22m4u/js-service");
  1650. var import_http4 = require("http");
  1651. // src/senders/data-sender.js
  1652. var import_js_format18 = require("@e22m4u/js-format");
  1653. var _DataSender = class _DataSender extends DebuggableService {
  1654. /**
  1655. * Send.
  1656. *
  1657. * @param {import('http').ServerResponse} response
  1658. * @param {*} data
  1659. * @returns {undefined}
  1660. */
  1661. send(response, data) {
  1662. const debug = this.getDebuggerFor(this.send);
  1663. if (data === response || response.headersSent) {
  1664. debug(
  1665. "Response sending was skipped because its headers where sent already."
  1666. );
  1667. return;
  1668. }
  1669. if (data == null) {
  1670. response.statusCode = 204;
  1671. response.end();
  1672. debug("The empty response was sent.");
  1673. return;
  1674. }
  1675. if (isReadableStream(data)) {
  1676. response.setHeader("Content-Type", "application/octet-stream");
  1677. data.pipe(response);
  1678. debug("The stream response was sent.");
  1679. return;
  1680. }
  1681. let debugMsg;
  1682. switch (typeof data) {
  1683. case "object":
  1684. case "boolean":
  1685. case "number":
  1686. if (Buffer.isBuffer(data)) {
  1687. response.setHeader("content-type", "application/octet-stream");
  1688. debugMsg = "The Buffer was sent as binary data.";
  1689. } else {
  1690. response.setHeader("content-type", "application/json");
  1691. debugMsg = (0, import_js_format18.format)("The %v was sent as JSON.", typeof data);
  1692. data = JSON.stringify(data);
  1693. }
  1694. break;
  1695. default:
  1696. response.setHeader("content-type", "text/plain");
  1697. debugMsg = "The response data was sent as plain text.";
  1698. data = String(data);
  1699. break;
  1700. }
  1701. response.end(data);
  1702. debug(debugMsg);
  1703. }
  1704. };
  1705. __name(_DataSender, "DataSender");
  1706. var DataSender = _DataSender;
  1707. // src/senders/error-sender.js
  1708. var import_util = require("util");
  1709. var import_statuses = __toESM(require("statuses"), 1);
  1710. var EXPOSED_ERROR_PROPERTIES = ["code", "details"];
  1711. var _ErrorSender = class _ErrorSender extends DebuggableService {
  1712. /**
  1713. * Handle.
  1714. *
  1715. * @param {import('http').IncomingMessage} request
  1716. * @param {import('http').ServerResponse} response
  1717. * @param {Error} error
  1718. * @returns {undefined}
  1719. */
  1720. send(request, response, error) {
  1721. const debug = this.getDebuggerFor(this.send);
  1722. let safeError = {};
  1723. if (error) {
  1724. if (typeof error === "object") {
  1725. safeError = error;
  1726. } else {
  1727. safeError = { message: String(error) };
  1728. }
  1729. }
  1730. const statusCode = error.statusCode || error.status || 500;
  1731. const body = { error: {} };
  1732. if (safeError.message && typeof safeError.message === "string") {
  1733. body.error.message = safeError.message;
  1734. } else {
  1735. body.error.message = (0, import_statuses.default)(statusCode);
  1736. }
  1737. EXPOSED_ERROR_PROPERTIES.forEach((name) => {
  1738. if (name in safeError) {
  1739. body.error[name] = safeError[name];
  1740. }
  1741. });
  1742. const requestData = {
  1743. url: request.url,
  1744. method: request.method,
  1745. headers: request.headers
  1746. };
  1747. const inspectOptions = {
  1748. showHidden: false,
  1749. depth: null,
  1750. colors: true,
  1751. compact: false
  1752. };
  1753. console.warn((0, import_util.inspect)(requestData, inspectOptions));
  1754. console.warn((0, import_util.inspect)(body, inspectOptions));
  1755. if (error.stack) {
  1756. console.log(error.stack);
  1757. } else {
  1758. console.error(error);
  1759. }
  1760. response.statusCode = statusCode;
  1761. response.setHeader("content-type", "application/json; charset=utf-8");
  1762. response.end(JSON.stringify(body, null, 2), "utf-8");
  1763. debug(
  1764. "The %s error was sent for the request %s %v.",
  1765. statusCode,
  1766. request.method,
  1767. getRequestPathname(request)
  1768. );
  1769. }
  1770. /**
  1771. * Send 404.
  1772. *
  1773. * @param {import('http').IncomingMessage} request
  1774. * @param {import('http').ServerResponse} response
  1775. * @returns {undefined}
  1776. */
  1777. send404(request, response) {
  1778. const debug = this.getDebuggerFor(this.send404);
  1779. response.statusCode = 404;
  1780. response.setHeader("content-type", "text/plain; charset=utf-8");
  1781. response.end("404 Not Found", "utf-8");
  1782. debug(
  1783. "The 404 error was sent for the request %s %v.",
  1784. request.method,
  1785. getRequestPathname(request)
  1786. );
  1787. }
  1788. };
  1789. __name(_ErrorSender, "ErrorSender");
  1790. var ErrorSender = _ErrorSender;
  1791. // src/trie-router.js
  1792. var _TrieRouter = class _TrieRouter extends DebuggableService {
  1793. /**
  1794. * Define route.
  1795. *
  1796. * Example 1:
  1797. * ```
  1798. * const router = new TrieRouter();
  1799. * router.defineRoute({
  1800. * method: HttpMethod.GET, // Request method.
  1801. * path: '/', // Path template.
  1802. * handler: ctx => 'Hello world!', // Request handler.
  1803. * });
  1804. * ```
  1805. *
  1806. * Example 2:
  1807. * ```
  1808. * const router = new TrieRouter();
  1809. * router.defineRoute({
  1810. * method: HttpMethod.POST, // Request method.
  1811. * path: '/users/:id', // The path template may have parameters.
  1812. * preHandler(ctx) { ... }, // The "preHandler" executes before a route handler.
  1813. * handler(ctx) { ... }, // Request handler function.
  1814. * postHandler(ctx, data) { ... }, // The "postHandler" executes after a route handler.
  1815. * });
  1816. * ```
  1817. *
  1818. * @param {import('./route-registry.js').RouteDefinition} routeDef
  1819. * @returns {import('./route/index.js').Route}
  1820. */
  1821. defineRoute(routeDef) {
  1822. return this.getService(RouteRegistry).defineRoute(routeDef);
  1823. }
  1824. /**
  1825. * Request listener.
  1826. *
  1827. * Example:
  1828. * ```
  1829. * import http from 'http';
  1830. * import {TrieRouter} from '@e22m4u/js-trie-router';
  1831. *
  1832. * const router = new TrieRouter();
  1833. * const server = new http.Server();
  1834. * server.on('request', router.requestListener); // Sets the request listener.
  1835. * server.listen(3000); // Starts listening for connections.
  1836. * ```
  1837. *
  1838. * @returns {Function}
  1839. */
  1840. get requestListener() {
  1841. return this._handleRequest.bind(this);
  1842. }
  1843. /**
  1844. * Handle incoming request.
  1845. *
  1846. * @param {import('http').IncomingMessage} request
  1847. * @param {import('http').ServerResponse} response
  1848. * @returns {Promise<undefined>}
  1849. * @private
  1850. */
  1851. async _handleRequest(request, response) {
  1852. const debug = this.getDebuggerFor(this._handleRequest);
  1853. const requestPath = (request.url || "/").replace(/\?.*$/, "");
  1854. debug(
  1855. "Preparing to handle an incoming request %s %v.",
  1856. request.method,
  1857. requestPath
  1858. );
  1859. const resolved = this.getService(RouteRegistry).matchRouteByRequest(request);
  1860. if (!resolved) {
  1861. debug("No route for the request %s %v.", request.method, requestPath);
  1862. this.getService(ErrorSender).send404(request, response);
  1863. } else {
  1864. const { route, params } = resolved;
  1865. const container = new import_js_service4.ServiceContainer(this.container);
  1866. const context = new RequestContext(container, request, response, route);
  1867. container.set(RequestContext, context);
  1868. container.set(import_http4.IncomingMessage, request);
  1869. container.set(import_http4.ServerResponse, response);
  1870. context.params = params;
  1871. let data;
  1872. try {
  1873. const reqDataOrPromise = this.getService(RequestParser).parse(request);
  1874. if (isPromise(reqDataOrPromise)) {
  1875. const reqData = await reqDataOrPromise;
  1876. Object.assign(context, reqData);
  1877. } else {
  1878. Object.assign(context, reqDataOrPromise);
  1879. }
  1880. const hookInvoker = this.getService(HookInvoker);
  1881. data = hookInvoker.invokeAndContinueUntilValueReceived(
  1882. route,
  1883. RouterHookType.PRE_HANDLER,
  1884. response,
  1885. context
  1886. );
  1887. if (isPromise(data)) {
  1888. data = await data;
  1889. }
  1890. if (!isResponseSent(response) && data == null) {
  1891. data = route.handle(context);
  1892. if (isPromise(data)) {
  1893. data = await data;
  1894. }
  1895. let postHandlerData = hookInvoker.invokeAndContinueUntilValueReceived(
  1896. route,
  1897. RouterHookType.POST_HANDLER,
  1898. response,
  1899. context,
  1900. data
  1901. );
  1902. if (isPromise(postHandlerData)) {
  1903. postHandlerData = await postHandlerData;
  1904. }
  1905. if (postHandlerData != null) {
  1906. data = postHandlerData;
  1907. }
  1908. }
  1909. } catch (error) {
  1910. this.getService(ErrorSender).send(request, response, error);
  1911. return;
  1912. }
  1913. if (!isResponseSent(response)) {
  1914. this.getService(DataSender).send(response, data);
  1915. }
  1916. }
  1917. }
  1918. /**
  1919. * Add hook.
  1920. *
  1921. * Example:
  1922. * ```
  1923. * import {TrieRouter} from '@e22m4u/js-trie-router';
  1924. * import {RouterHookType} from '@e22m4u/js-trie-router';
  1925. *
  1926. * // Router instance.
  1927. * const router = new TrieRouter();
  1928. *
  1929. * // Adds the "preHandler" hook for each route.
  1930. * router.addHook(
  1931. * RouterHookType.PRE_HANDLER,
  1932. * ctx => { ... },
  1933. * );
  1934. *
  1935. * // Adds the "postHandler" hook for each route.
  1936. * router.addHook(
  1937. * RouterHookType.POST_HANDLER,
  1938. * ctx => { ... },
  1939. * );
  1940. * ```
  1941. *
  1942. * @param {string} type
  1943. * @param {Function} hook
  1944. * @returns {this}
  1945. */
  1946. addHook(type, hook) {
  1947. this.getService(HookRegistry).addHook(type, hook);
  1948. return this;
  1949. }
  1950. /**
  1951. * Add pre-handler hook.
  1952. *
  1953. * @param {Function} hook
  1954. * @returns {this}
  1955. */
  1956. addPreHandler(hook) {
  1957. this.getService(HookRegistry).addHook(RouterHookType.PRE_HANDLER, hook);
  1958. return this;
  1959. }
  1960. /**
  1961. * Add post-handler hook.
  1962. *
  1963. * @param {Function} hook
  1964. * @returns {this}
  1965. */
  1966. addPostHandler(hook) {
  1967. this.getService(HookRegistry).addHook(RouterHookType.POST_HANDLER, hook);
  1968. return this;
  1969. }
  1970. };
  1971. __name(_TrieRouter, "TrieRouter");
  1972. var TrieRouter = _TrieRouter;
  1973. // Annotate the CommonJS export names for ESM import in node:
  1974. 0 && (module.exports = {
  1975. BodyParser,
  1976. CHARACTER_ENCODING_LIST,
  1977. CookiesParser,
  1978. DataSender,
  1979. EXPOSED_ERROR_PROPERTIES,
  1980. ErrorSender,
  1981. HookInvoker,
  1982. HookRegistry,
  1983. HttpMethod,
  1984. METHODS_WITH_BODY,
  1985. QueryParser,
  1986. ROOT_PATH,
  1987. RequestContext,
  1988. RequestParser,
  1989. Route,
  1990. RouteRegistry,
  1991. RouterHookType,
  1992. RouterOptions,
  1993. TrieRouter,
  1994. UNPARSABLE_MEDIA_TYPES,
  1995. cloneDeep,
  1996. createCookieString,
  1997. createError,
  1998. createRequestMock,
  1999. createResponseMock,
  2000. createRouteMock,
  2001. fetchRequestBody,
  2002. getRequestPathname,
  2003. isPromise,
  2004. isReadableStream,
  2005. isResponseSent,
  2006. isWritableStream,
  2007. parseContentType,
  2008. parseCookieString,
  2009. parseJsonBody,
  2010. toCamelCase,
  2011. validateRouteDefinition
  2012. });