index.cjs 59 KB

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