{"version":3,"file":"index.js","sources":["../src/headers.ts","../src/append.ts","../src/cookie.ts","../src/util.ts","../src/format.ts","../src/redirect.ts","../src/download.ts"],"sourcesContent":["import { IncomingMessage as Req, ServerResponse as Res } from 'node:http'\nimport mime from 'mime'\nimport { getRequestHeader } from '@tinyhttp/req'\nimport { vary } from '@tinyhttp/vary'\nimport { encodeUrl } from '@tinyhttp/encode-url'\n\nconst charsetRegExp = /;\\s*charset\\s*=/\n\nexport const setHeader =\n (res: Response) =>\n (field: string | Record, val?: string | number | readonly string[]): Response => {\n if (typeof field === 'string') {\n let value = Array.isArray(val) ? val.map(String) : String(val)\n\n // add charset to content-type\n if (field.toLowerCase() === 'content-type') {\n if (Array.isArray(value)) {\n throw new TypeError('Content-Type cannot be set to an Array')\n }\n\n if (!charsetRegExp.test(value)) {\n const charset = 'UTF-8' // UTF-8 is the default charset for all types\n\n if (typeof charset === 'string') value += '; charset=' + charset.toLowerCase()\n }\n }\n\n res.setHeader(field, value)\n } else {\n for (const key in field) {\n setHeader(res)(key, field[key] as string)\n }\n }\n return res\n }\n\nexport const setLocationHeader =\n (req: Request, res: Response) =>\n (url: string): Response => {\n let loc = url\n\n // \"back\" is an alias for the referrer\n if (url === 'back') loc = (getRequestHeader(req)('Referrer') as string) || '/'\n\n // set location\n res.setHeader('Location', encodeUrl(loc))\n return res\n }\n\nexport const getResponseHeader =\n (res: Response) =>\n (field: string): string | number | string[] => {\n return res.getHeader(field)\n }\n\nexport const setLinksHeader =\n (res: Response) =>\n (links: { [key: string]: string }): Response => {\n let link = res.getHeader('Link') || ''\n if (link) link += ', '\n res.setHeader(\n 'Link',\n link +\n Object.keys(links)\n .map((rel) => '<' + links[rel] + '>; rel=\"' + rel + '\"')\n .join(', ')\n )\n\n return res\n }\n\nexport const setVaryHeader =\n (res: Response) =>\n (field: string): Response => {\n vary(res, field)\n\n return res\n }\n\nexport const setContentType =\n (res: Response) =>\n (type: string): Response => {\n const ct = type.indexOf('/') === -1 ? mime.getType(type) : type\n\n setHeader(res)('Content-Type', ct)\n\n return res\n }\n","import { ServerResponse as Res } from 'node:http'\nimport { getResponseHeader, setHeader } from './headers.js'\n\nexport const append =\n (res: Response) =>\n (field: string, value: string | number | string[]): Response => {\n const prevVal = getResponseHeader(res)(field)\n let newVal = value\n\n if (prevVal && typeof newVal !== 'number' && typeof prevVal !== 'number') {\n newVal = Array.isArray(prevVal)\n ? prevVal.concat(newVal)\n : Array.isArray(newVal)\n ? [prevVal].concat(newVal)\n : [prevVal, newVal]\n }\n setHeader(res)(field, newVal)\n return res\n }\n","import { IncomingMessage as Req, ServerResponse as Res } from 'node:http'\nimport * as cookie from '@tinyhttp/cookie'\nimport { sign } from '@tinyhttp/cookie-signature'\nimport { append } from './append.js'\n\nexport const setCookie =\n (\n req: Request & {\n secret?: string | string[]\n },\n res: Response\n ) =>\n (\n name: string,\n value: string | Record,\n options: cookie.SerializeOptions &\n Partial<{\n signed: boolean\n }> = {}\n ): Response => {\n const secret = req.secret as string\n\n const signed = options.signed || false\n\n if (signed && !secret) throw new Error('cookieParser(\"secret\") required for signed cookies')\n\n let val = typeof value === 'object' ? 'j:' + JSON.stringify(value) : String(value)\n\n if (signed) val = 's:' + sign(val, secret)\n\n if (options.maxAge) {\n options.expires = new Date(Date.now() + options.maxAge)\n options.maxAge /= 1000\n }\n\n if (options.path == null) options.path = '/'\n\n append(res)('Set-Cookie', `${cookie.serialize(name, String(val), options)}`)\n\n return res\n }\n\nexport const clearCookie =\n (req: Request, res: Response) =>\n (name: string, options?: cookie.SerializeOptions): Response => {\n return setCookie(req, res)(name, '', Object.assign({}, { expires: new Date(1), path: '/' }, options))\n }\n","import mime from 'mime'\n\nexport type NormalizedType = {\n value: string\n quality?: number\n params: Record\n originalIndex?: number\n}\n\nexport const normalizeType = (type: string): NormalizedType =>\n ~type.indexOf('/') ? acceptParams(type) : { value: mime.getType(type), params: {} }\n\nexport function acceptParams(str: string, index?: number): NormalizedType {\n const parts = str.split(/ *; */)\n const ret: NormalizedType = { value: parts[0], quality: 1, params: {}, originalIndex: index }\n\n for (const part of parts) {\n const pms = part.split(/ *= */)\n if ('q' === pms[0]) ret.quality = parseFloat(pms[1])\n else ret.params[pms[0]] = pms[1]\n }\n\n return ret\n}\n\nexport function normalizeTypes(types: string[]): NormalizedType[] {\n const ret: NormalizedType[] = []\n\n for (const type of types) {\n ret.push(normalizeType(type))\n }\n\n return ret\n}\n","import { IncomingMessage as Req, ServerResponse as Res } from 'node:http'\nimport { getAccepts } from '@tinyhttp/req'\nimport { setVaryHeader } from './headers.js'\nimport { normalizeType, normalizeTypes } from './util.js'\n\nexport type FormatProps = {\n default?: () => void\n} & Record\n\nexport type FormatError = Error & {\n status: number\n statusCode: number\n types: string[]\n}\n\ntype next = (err?: FormatError) => void\n\nexport const formatResponse =\n (\n req: Request,\n res: Response,\n next: Next\n ) =>\n (obj: FormatProps): Response => {\n const fn = obj.default\n\n if (fn) delete obj.default\n\n const keys = Object.keys(obj)\n\n const key = keys.length > 0 ? (getAccepts(req)(...keys) as string) : false\n\n setVaryHeader(res)('Accept')\n\n if (key) {\n res.setHeader('Content-Type', normalizeType(key).value)\n obj[key](req, res, next)\n } else if (fn) {\n fn()\n } else {\n const err = new Error('Not Acceptable') as FormatError\n err.status = err.statusCode = 406\n err.types = normalizeTypes(keys).map((o) => o.value)\n\n next(err)\n }\n\n return res\n }\n","import { IncomingMessage as Req, ServerResponse as Res, STATUS_CODES } from 'node:http'\nimport { escapeHTML } from 'es-escape-html'\nimport { formatResponse } from './format.js'\nimport { setLocationHeader } from './headers.js'\n\ntype next = (err?: any) => void\n\nexport const redirect =\n (\n req: Request,\n res: Response,\n next: Next\n ) =>\n (url: string, status?: number): Response => {\n let address = url\n status = status || 302\n\n let body = ''\n\n address = setLocationHeader(req, res)(address).getHeader('Location') as string\n\n formatResponse(\n req,\n res,\n next\n )({\n text: () => {\n body = STATUS_CODES[status] + '. Redirecting to ' + address\n },\n html: () => {\n const u = escapeHTML(address)\n\n body = `

${STATUS_CODES[status]}. Redirecting to ${u}

`\n },\n default: () => {\n body = ''\n }\n })\n\n res.setHeader('Content-Length', Buffer.byteLength(body))\n\n res.statusCode = status\n\n if (req.method === 'HEAD') res.end()\n else res.end(body)\n\n return res\n }\n","import { contentDisposition } from '@tinyhttp/content-disposition'\nimport { sendFile } from '@tinyhttp/send'\nimport { extname, resolve, basename } from 'node:path'\nimport { IncomingMessage as Req, ServerResponse as Res } from 'node:http'\nimport { setContentType, setHeader } from './headers.js'\nimport type { SendFileOptions } from '@tinyhttp/send'\n\nexport type DownloadOptions = SendFileOptions &\n Partial<{\n headers: Record\n }>\n\ntype Callback = (err?: any) => void\n\nexport const download =\n (req: Request, res: Response) =>\n (path: string, filename?: string | Callback, options?: DownloadOptions | Callback, cb?: Callback): Response => {\n let done = cb\n let name = filename as string\n let opts = (options || null) as DownloadOptions\n\n // support function as second or third arg\n if (typeof filename === 'function') {\n done = filename\n name = null\n } else if (typeof options === 'function') {\n done = options\n opts = null\n }\n\n // set Content-Disposition when file is sent\n const headers = {\n 'Content-Disposition': contentDisposition(name || basename(path))\n }\n\n // merge user-provided headers\n if (opts && opts.headers) {\n for (const key of Object.keys(opts.headers)) {\n if (key.toLowerCase() !== 'content-disposition') headers[key] = opts.headers[key]\n }\n }\n\n // merge user-provided options\n opts = { ...opts, headers }\n\n // send file\n\n return sendFile(req, res)(opts.root ? path : resolve(path), opts, done || (() => undefined))\n }\n\nexport const attachment =\n (res: Response) =>\n (filename?: string): Response => {\n if (filename) {\n setContentType(res)(extname(filename))\n filename = basename(filename)\n }\n\n setHeader(res)('Content-Disposition', contentDisposition(filename))\n\n return res\n }\n"],"names":[],"mappings":"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"}