import type { InputToData, InputToTypeData, ValidationTypes } from './types.ts' import { parseBody } from './utils/body.ts' import type { BodyData } from './utils/body.ts' import type { Cookie } from './utils/cookie.ts' import { parse } from './utils/cookie.ts' import { mergeObjects } from './utils/object.ts' import type { UnionToIntersection } from './utils/types.ts' import { getQueryStringFromURL, getQueryParam, getQueryParams } from './utils/url.ts' // eslint-disable-next-line @typescript-eslint/no-unused-vars type ParamKeyName = NameWithPattern extends `${infer Name}{${infer _Pattern}` ? Name : NameWithPattern type ParamKey = Component extends `:${infer NameWithPattern}` ? ParamKeyName : never type ParamKeys = Path extends `${infer Component}/${infer Rest}` ? ParamKey | ParamKeys : ParamKey type RemoveQuestion = T extends `${infer R}?` ? R : T // eslint-disable-next-line @typescript-eslint/no-unused-vars type UndefinedIfHavingQuestion = T extends `${infer _}?` ? string | undefined : string type ParamKeyToRecord = T extends `${infer R}?` ? Record : Record export class HonoRequest { raw: Request private paramData: Record | undefined private headerData: Record | undefined private bodyData: BodyData | undefined // eslint-disable-next-line @typescript-eslint/no-explicit-any private jsonData: Promise | undefined private validatedData: { [K in keyof ValidationTypes]?: {} } private queryIndex: number constructor( request: Request, paramData?: Record | undefined, queryIndex: number = -1 ) { this.raw = request this.paramData = paramData this.queryIndex = queryIndex this.validatedData = {} } param(key: RemoveQuestion>): UndefinedIfHavingQuestion> param(): UnionToIntersection>> param(key?: string): unknown { if (this.paramData) { if (key) { const param = this.paramData[key] return param ? (/\%/.test(param) ? decodeURIComponent(param) : param) : undefined } else { const decoded: Record = {} for (const [key, value] of Object.entries(this.paramData)) { if (value && typeof value === 'string') { decoded[key] = /\%/.test(value) ? decodeURIComponent(value) : value } } return decoded } } return null } query(key: string): string query(): Record query(key?: string) { const queryString = getQueryStringFromURL(this.url, this.queryIndex) return getQueryParam(queryString, key) } queries(key: string): string[] queries(): Record queries(key?: string) { const queryString = getQueryStringFromURL(this.url, this.queryIndex) return getQueryParams(queryString, key) } header(name: string): string header(): Record header(name?: string) { if (!this.headerData) { this.headerData = {} this.raw.headers.forEach((value, key) => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.headerData![key] = value }) } if (name) { return this.headerData[name.toLowerCase()] } else { return this.headerData } } cookie(key: string): string | undefined cookie(): Cookie cookie(key?: string) { const cookie = this.raw.headers.get('Cookie') if (!cookie) return const obj = parse(cookie) if (key) { const value = obj[key] return value } else { return obj } } async parseBody(): Promise { // Cache the parsed body let body: BodyData if (!this.bodyData) { body = await parseBody(this.raw) this.bodyData = body } else { body = this.bodyData } return body } async json() { // Cache the JSON body let jsonData: Promise> if (!this.jsonData) { jsonData = this.raw.json() this.jsonData = jsonData } else { jsonData = this.jsonData } return jsonData } async text() { return this.raw.text() } async arrayBuffer() { return this.raw.arrayBuffer() } async blob() { return this.raw.blob() } async formData() { return this.raw.formData() } addValidatedData(type: keyof ValidationTypes, data: {}) { const storedData = this.validatedData[type] || {} const merged = mergeObjects(storedData, data) this.validatedData[type] = merged } valid(): InputToData valid(type: T): InputToTypeData valid(type?: T) { if (type) { const data = this.validatedData[type] return data } else { let data: Record = {} for (const v of Object.values(this.validatedData)) { data = mergeObjects(data, v) } return data } } get url() { return this.raw.url } get method() { return this.raw.method } get headers() { return this.raw.headers } get redirect() { return this.raw.redirect } get body() { return this.raw.body } get bodyUsed() { return this.raw.bodyUsed } get cache() { return this.raw.cache } get credentials() { return this.raw.credentials } get integrity() { return this.raw.integrity } get keepalive() { return this.raw.keepalive } get mode() { return this.raw.mode } get referrer() { return this.raw.referrer } get refererPolicy() { return this.raw.referrerPolicy } get signal() { return this.raw.signal } }