request.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. var __defProp = Object.defineProperty;
  2. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  3. var __getOwnPropNames = Object.getOwnPropertyNames;
  4. var __hasOwnProp = Object.prototype.hasOwnProperty;
  5. var __export = (target, all) => {
  6. for (var name in all)
  7. __defProp(target, name, { get: all[name], enumerable: true });
  8. };
  9. var __copyProps = (to, from, except, desc) => {
  10. if (from && typeof from === "object" || typeof from === "function") {
  11. for (let key of __getOwnPropNames(from))
  12. if (!__hasOwnProp.call(to, key) && key !== except)
  13. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  14. }
  15. return to;
  16. };
  17. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  18. var request_exports = {};
  19. __export(request_exports, {
  20. HonoRequest: () => HonoRequest,
  21. cloneRawRequest: () => cloneRawRequest
  22. });
  23. module.exports = __toCommonJS(request_exports);
  24. var import_http_exception = require("./http-exception");
  25. var import_constants = require("./request/constants");
  26. var import_body = require("./utils/body");
  27. var import_url = require("./utils/url");
  28. const tryDecodeURIComponent = (str) => (0, import_url.tryDecode)(str, import_url.decodeURIComponent_);
  29. class HonoRequest {
  30. /**
  31. * `.raw` can get the raw Request object.
  32. *
  33. * @see {@link https://hono.dev/docs/api/request#raw}
  34. *
  35. * @example
  36. * ```ts
  37. * // For Cloudflare Workers
  38. * app.post('/', async (c) => {
  39. * const metadata = c.req.raw.cf?.hostMetadata?
  40. * ...
  41. * })
  42. * ```
  43. */
  44. raw;
  45. #validatedData;
  46. // Short name of validatedData
  47. #matchResult;
  48. routeIndex = 0;
  49. /**
  50. * `.path` can get the pathname of the request.
  51. *
  52. * @see {@link https://hono.dev/docs/api/request#path}
  53. *
  54. * @example
  55. * ```ts
  56. * app.get('/about/me', (c) => {
  57. * const pathname = c.req.path // `/about/me`
  58. * })
  59. * ```
  60. */
  61. path;
  62. bodyCache = {};
  63. constructor(request, path = "/", matchResult = [[]]) {
  64. this.raw = request;
  65. this.path = path;
  66. this.#matchResult = matchResult;
  67. this.#validatedData = {};
  68. }
  69. param(key) {
  70. return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();
  71. }
  72. #getDecodedParam(key) {
  73. const paramKey = this.#matchResult[0][this.routeIndex][1][key];
  74. const param = this.#getParamValue(paramKey);
  75. return param && /\%/.test(param) ? tryDecodeURIComponent(param) : param;
  76. }
  77. #getAllDecodedParams() {
  78. const decoded = {};
  79. const keys = Object.keys(this.#matchResult[0][this.routeIndex][1]);
  80. for (const key of keys) {
  81. const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
  82. if (value !== void 0) {
  83. decoded[key] = /\%/.test(value) ? tryDecodeURIComponent(value) : value;
  84. }
  85. }
  86. return decoded;
  87. }
  88. #getParamValue(paramKey) {
  89. return this.#matchResult[1] ? this.#matchResult[1][paramKey] : paramKey;
  90. }
  91. query(key) {
  92. return (0, import_url.getQueryParam)(this.url, key);
  93. }
  94. queries(key) {
  95. return (0, import_url.getQueryParams)(this.url, key);
  96. }
  97. header(name) {
  98. if (name) {
  99. return this.raw.headers.get(name) ?? void 0;
  100. }
  101. const headerData = {};
  102. this.raw.headers.forEach((value, key) => {
  103. headerData[key] = value;
  104. });
  105. return headerData;
  106. }
  107. async parseBody(options) {
  108. return (0, import_body.parseBody)(this, options);
  109. }
  110. #cachedBody = (key) => {
  111. const { bodyCache, raw } = this;
  112. const cachedBody = bodyCache[key];
  113. if (cachedBody) {
  114. return cachedBody;
  115. }
  116. const anyCachedKey = Object.keys(bodyCache)[0];
  117. if (anyCachedKey) {
  118. return bodyCache[anyCachedKey].then((body) => {
  119. if (anyCachedKey === "json") {
  120. body = JSON.stringify(body);
  121. }
  122. return new Response(body)[key]();
  123. });
  124. }
  125. return bodyCache[key] = raw[key]();
  126. };
  127. /**
  128. * `.json()` can parse Request body of type `application/json`
  129. *
  130. * @see {@link https://hono.dev/docs/api/request#json}
  131. *
  132. * @example
  133. * ```ts
  134. * app.post('/entry', async (c) => {
  135. * const body = await c.req.json()
  136. * })
  137. * ```
  138. */
  139. json() {
  140. return this.#cachedBody("text").then((text) => JSON.parse(text));
  141. }
  142. /**
  143. * `.text()` can parse Request body of type `text/plain`
  144. *
  145. * @see {@link https://hono.dev/docs/api/request#text}
  146. *
  147. * @example
  148. * ```ts
  149. * app.post('/entry', async (c) => {
  150. * const body = await c.req.text()
  151. * })
  152. * ```
  153. */
  154. text() {
  155. return this.#cachedBody("text");
  156. }
  157. /**
  158. * `.arrayBuffer()` parse Request body as an `ArrayBuffer`
  159. *
  160. * @see {@link https://hono.dev/docs/api/request#arraybuffer}
  161. *
  162. * @example
  163. * ```ts
  164. * app.post('/entry', async (c) => {
  165. * const body = await c.req.arrayBuffer()
  166. * })
  167. * ```
  168. */
  169. arrayBuffer() {
  170. return this.#cachedBody("arrayBuffer");
  171. }
  172. /**
  173. * Parses the request body as a `Blob`.
  174. * @example
  175. * ```ts
  176. * app.post('/entry', async (c) => {
  177. * const body = await c.req.blob();
  178. * });
  179. * ```
  180. * @see https://hono.dev/docs/api/request#blob
  181. */
  182. blob() {
  183. return this.#cachedBody("blob");
  184. }
  185. /**
  186. * Parses the request body as `FormData`.
  187. * @example
  188. * ```ts
  189. * app.post('/entry', async (c) => {
  190. * const body = await c.req.formData();
  191. * });
  192. * ```
  193. * @see https://hono.dev/docs/api/request#formdata
  194. */
  195. formData() {
  196. return this.#cachedBody("formData");
  197. }
  198. /**
  199. * Adds validated data to the request.
  200. *
  201. * @param target - The target of the validation.
  202. * @param data - The validated data to add.
  203. */
  204. addValidatedData(target, data) {
  205. this.#validatedData[target] = data;
  206. }
  207. valid(target) {
  208. return this.#validatedData[target];
  209. }
  210. /**
  211. * `.url()` can get the request url strings.
  212. *
  213. * @see {@link https://hono.dev/docs/api/request#url}
  214. *
  215. * @example
  216. * ```ts
  217. * app.get('/about/me', (c) => {
  218. * const url = c.req.url // `http://localhost:8787/about/me`
  219. * ...
  220. * })
  221. * ```
  222. */
  223. get url() {
  224. return this.raw.url;
  225. }
  226. /**
  227. * `.method()` can get the method name of the request.
  228. *
  229. * @see {@link https://hono.dev/docs/api/request#method}
  230. *
  231. * @example
  232. * ```ts
  233. * app.get('/about/me', (c) => {
  234. * const method = c.req.method // `GET`
  235. * })
  236. * ```
  237. */
  238. get method() {
  239. return this.raw.method;
  240. }
  241. get [import_constants.GET_MATCH_RESULT]() {
  242. return this.#matchResult;
  243. }
  244. /**
  245. * `.matchedRoutes()` can return a matched route in the handler
  246. *
  247. * @deprecated
  248. *
  249. * Use matchedRoutes helper defined in "hono/route" instead.
  250. *
  251. * @see {@link https://hono.dev/docs/api/request#matchedroutes}
  252. *
  253. * @example
  254. * ```ts
  255. * app.use('*', async function logger(c, next) {
  256. * await next()
  257. * c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {
  258. * const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]')
  259. * console.log(
  260. * method,
  261. * ' ',
  262. * path,
  263. * ' '.repeat(Math.max(10 - path.length, 0)),
  264. * name,
  265. * i === c.req.routeIndex ? '<- respond from here' : ''
  266. * )
  267. * })
  268. * })
  269. * ```
  270. */
  271. get matchedRoutes() {
  272. return this.#matchResult[0].map(([[, route]]) => route);
  273. }
  274. /**
  275. * `routePath()` can retrieve the path registered within the handler
  276. *
  277. * @deprecated
  278. *
  279. * Use routePath helper defined in "hono/route" instead.
  280. *
  281. * @see {@link https://hono.dev/docs/api/request#routepath}
  282. *
  283. * @example
  284. * ```ts
  285. * app.get('/posts/:id', (c) => {
  286. * return c.json({ path: c.req.routePath })
  287. * })
  288. * ```
  289. */
  290. get routePath() {
  291. return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path;
  292. }
  293. }
  294. const cloneRawRequest = async (req) => {
  295. if (!req.raw.bodyUsed) {
  296. return req.raw.clone();
  297. }
  298. const cacheKey = Object.keys(req.bodyCache)[0];
  299. if (!cacheKey) {
  300. throw new import_http_exception.HTTPException(500, {
  301. message: "Cannot clone request: body was already consumed and not cached. Please use HonoRequest methods (e.g., req.json(), req.text()) instead of consuming req.raw directly."
  302. });
  303. }
  304. const requestInit = {
  305. body: await req[cacheKey](),
  306. cache: req.raw.cache,
  307. credentials: req.raw.credentials,
  308. headers: req.header(),
  309. integrity: req.raw.integrity,
  310. keepalive: req.raw.keepalive,
  311. method: req.method,
  312. mode: req.raw.mode,
  313. redirect: req.raw.redirect,
  314. referrer: req.raw.referrer,
  315. referrerPolicy: req.raw.referrerPolicy,
  316. signal: req.raw.signal
  317. };
  318. return new Request(req.url, requestInit);
  319. };
  320. // Annotate the CommonJS export names for ESM import in node:
  321. 0 && (module.exports = {
  322. HonoRequest,
  323. cloneRawRequest
  324. });