{"version":3,"file":"index.js","sources":["../src/json.ts","../src/utils.ts","../src/send.ts","../src/sendStatus.ts","../src/status.ts","../src/sendFile.ts"],"sourcesContent":["import { ServerResponse as S } from 'node:http'\n\ntype Res = Pick\n\n/**\n * Respond with stringified JSON object\n * @param res Response\n */\nexport const json =\n (res: Response) =>\n (body: any, ...args: any[]): Response => {\n res.setHeader('Content-Type', 'application/json')\n if (typeof body === 'object' && body != null) res.end(JSON.stringify(body, null, 2), ...args)\n else if (typeof body === 'string') res.end(body, ...args)\n else if (body == null) {\n res.removeHeader('Content-Length')\n res.removeHeader('Transfer-Encoding')\n res.end(null, ...args)\n }\n\n return res\n }\n","import { parse, format } from '@tinyhttp/content-type'\nimport { eTag } from '@tinyhttp/etag'\nimport { Stats } from 'node:fs'\n\nexport const createETag = (body: Buffer | string | Stats, encoding: BufferEncoding): string => {\n if (body instanceof Stats) {\n return eTag(body, { weak: true })\n } else {\n return eTag(!Buffer.isBuffer(body) ? Buffer.from(body, encoding) : body, { weak: true })\n }\n}\n\nexport function setCharset(type: string, charset: string): string {\n const parsed = parse(type)\n parsed.parameters.charset = charset\n return format(parsed)\n}\n","import type { IncomingMessage as I, ServerResponse as S } from 'node:http'\nimport { json } from './json.js'\nimport { setCharset, createETag } from './utils.js'\n\ntype Req = Pick & { fresh?: boolean }\n\ntype Res = Pick\n\n/**\n * Sends the HTTP response.\n *\n * The body parameter can be a Buffer object, a string, an object, or an array.\n *\n * This method performs many useful tasks for simple non-streaming responses.\n * For example, it automatically assigns the Content-Length HTTP response header field (unless previously defined) and provides automatic HEAD and HTTP cache freshness support.\n *\n * @param req Request\n * @param res Response\n */\nexport const send =\n (req: Request, res: Response) =>\n (body: any): Response => {\n let bodyToSend = body\n\n if (Buffer.isBuffer(body)) {\n bodyToSend = body\n } else if (typeof body === 'object' && body !== null) {\n // in case of object - turn it to json\n bodyToSend = JSON.stringify(body, null, 2)\n } else if (typeof body === 'string') {\n // reflect this in content-type\n const type = res.getHeader('Content-Type')\n\n if (type && typeof type === 'string') {\n res.setHeader('Content-Type', setCharset(type, 'utf-8'))\n } else res.setHeader('Content-Type', setCharset('text/html', 'utf-8'))\n }\n\n // Set encoding\n const encoding: 'utf8' | undefined = 'utf8'\n\n // populate ETag\n let etag: string | undefined\n if (body && !res.getHeader('etag') && (etag = createETag(bodyToSend, encoding))) {\n res.setHeader('etag', etag)\n }\n\n // freshness\n if (req.fresh) res.statusCode = 304\n\n // strip irrelevant headers\n if (res.statusCode === 204 || res.statusCode === 304) {\n res.removeHeader('Content-Type')\n res.removeHeader('Content-Length')\n res.removeHeader('Transfer-Encoding')\n bodyToSend = ''\n }\n\n if (req.method === 'HEAD') {\n res.end('')\n return res\n }\n\n if (typeof body === 'object') {\n if (body == null) {\n res.end('')\n return res\n } else if (Buffer.isBuffer(body)) {\n if (!res.getHeader('Content-Type')) res.setHeader('content-type', 'application/octet-stream')\n res.end(bodyToSend)\n } else json(res)(bodyToSend, encoding)\n } else {\n if (typeof bodyToSend !== 'string') bodyToSend = bodyToSend.toString()\n\n res.end(bodyToSend, encoding)\n }\n\n return res\n }\n","import { IncomingMessage as I, ServerResponse as S } from 'node:http'\nimport { STATUS_CODES } from 'node:http'\nimport { send } from './send.js'\n\ntype Req = Pick\n\ntype Res = Pick\n\n/**\n * Sets the response HTTP status code to statusCode and send its string representation as the response body.\n *\n * If an unsupported status code is specified, the HTTP status is still set to statusCode and the string version of the code is sent as the response body.\n *\n * @param req Request\n * @param res Response\n */\nexport const sendStatus =\n (req: Request, res: Response) =>\n (statusCode: number): Response => {\n const body = STATUS_CODES[statusCode] || String(statusCode)\n\n res.statusCode = statusCode\n\n res.setHeader('Content-Type', 'text/plain')\n\n return send(req, res)(body)\n }\n","import type { ServerResponse } from 'node:http'\n\ntype Res = Pick\n\n/**\n * Sets the HTTP status for the response. It is a chainable alias of Node’s `response.statusCode`.\n *\n * @param res Response\n */\nexport const status =\n (res: Response) =>\n (status: number): Response => {\n res.statusCode = status\n\n return res\n }\n","import type { IncomingMessage as I, ServerResponse as S } from 'node:http'\nimport { createReadStream, statSync } from 'node:fs'\nimport { isAbsolute, extname } from 'node:path'\nimport { createETag } from './utils.js'\nimport { join } from 'node:path'\nimport mime from 'mime'\n\nexport type ReadStreamOptions = Partial<{\n flags: string\n encoding: BufferEncoding\n fd: number\n mode: number\n autoClose: boolean\n emitClose: boolean\n start: number\n end: number\n highWaterMark: number\n}>\n\nexport type SendFileOptions = ReadStreamOptions &\n Partial<{\n root: string\n headers: Record\n caching: Partial<{\n maxAge: number\n immutable: boolean\n }>\n }>\n\nexport type Caching = Partial<{\n maxAge: number\n immutable: boolean\n}>\n\ntype Req = Pick\n\ntype Res = Pick & NodeJS.WritableStream\n\nexport const enableCaching = (res: Res, caching: Caching): void => {\n let cc = caching.maxAge != null && `public,max-age=${caching.maxAge}`\n if (cc && caching.immutable) cc += ',immutable'\n else if (cc && caching.maxAge === 0) cc += ',must-revalidate'\n\n if (cc) res.setHeader('Cache-Control', cc)\n}\n\n/**\n * Sends a file by piping a stream to response.\n *\n * It also checks for extension to set a proper `Content-Type` header.\n *\n * Path argument must be absolute. To use a relative path, specify the `root` option first.\n *\n * @param res Response\n */\nexport const sendFile =\n (req: Request, res: Response) =>\n (path: string, opts: SendFileOptions = {}, cb?: (err?: any) => void): Response => {\n const { root, headers = {}, encoding = 'utf-8', caching, ...options } = opts\n\n if (!isAbsolute(path) && !root) throw new TypeError('path must be absolute')\n\n if (caching) enableCaching(res, caching)\n\n const filePath = root ? join(root, path) : path\n\n const stats = statSync(filePath)\n\n headers['Content-Encoding'] = encoding\n\n headers['Last-Modified'] = stats.mtime.toUTCString()\n\n headers['ETag'] = createETag(stats, encoding)\n\n if (!res.getHeader('Content-Type')) headers['Content-Type'] = mime.getType(extname(path)) + '; charset=utf-8'\n\n let status = res.statusCode || 200\n\n if (req.headers['range']) {\n status = 206\n const [x, y] = req.headers.range.replace('bytes=', '').split('-')\n const end = (options.end = parseInt(y, 10) || stats.size - 1)\n const start = (options.start = parseInt(x, 10) || 0)\n\n if (start >= stats.size || end >= stats.size) {\n res\n .writeHead(416, {\n 'Content-Range': `bytes */${stats.size}`\n })\n .end()\n return res\n }\n headers['Content-Range'] = `bytes ${start}-${end}/${stats.size}`\n headers['Content-Length'] = end - start + 1\n headers['Accept-Ranges'] = 'bytes'\n } else {\n headers['Content-Length'] = stats.size\n }\n\n for (const [k, v] of Object.entries(headers)) res.setHeader(k, v)\n\n res.writeHead(status, headers)\n\n const stream = createReadStream(filePath, options)\n\n if (cb) stream.on('error', (err) => cb(err)).on('end', () => cb())\n\n stream.pipe(res)\n\n return res\n }\n"],"names":["status"],"mappings":";;;;;;AAQO,MAAM,OACX,CAA6B,QAC7B,CAAC,SAAc,SAA0B;AACnC,MAAA,UAAU,gBAAgB,kBAAkB;AAC5C,MAAA,OAAO,SAAS,YAAY,QAAQ;AAAU,QAAA,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,GAAG,IAAI;AAAA,WACnF,OAAO,SAAS;AAAc,QAAA,IAAI,MAAM,GAAG,IAAI;AAAA,WAC/C,QAAQ,MAAM;AACrB,QAAI,aAAa,gBAAgB;AACjC,QAAI,aAAa,mBAAmB;AAChC,QAAA,IAAI,MAAM,GAAG,IAAI;AAAA,EACvB;AAEO,SAAA;AACT;ACjBW,MAAA,aAAa,CAAC,MAA+B,aAAqC;AAC7F,MAAI,gBAAgB,OAAO;AACzB,WAAO,KAAK,MAAM,EAAE,MAAM,KAAM,CAAA;AAAA,EAAA,OAC3B;AACL,WAAO,KAAK,CAAC,OAAO,SAAS,IAAI,IAAI,OAAO,KAAK,MAAM,QAAQ,IAAI,MAAM,EAAE,MAAM,KAAM,CAAA;AAAA,EACzF;AACF;AAEgB,SAAA,WAAW,MAAc,SAAyB;AAC1D,QAAA,SAAS,MAAM,IAAI;AACzB,SAAO,WAAW,UAAU;AAC5B,SAAO,OAAO,MAAM;AACtB;ACGO,MAAM,OACX,CAAwD,KAAc,QACtE,CAAC,SAAwB;AACvB,MAAI,aAAa;AAEb,MAAA,OAAO,SAAS,IAAI,GAAG;AACZ,iBAAA;AAAA,EACJ,WAAA,OAAO,SAAS,YAAY,SAAS,MAAM;AAEpD,iBAAa,KAAK,UAAU,MAAM,MAAM,CAAC;AAAA,EAAA,WAChC,OAAO,SAAS,UAAU;AAE7B,UAAA,OAAO,IAAI,UAAU,cAAc;AAErC,QAAA,QAAQ,OAAO,SAAS,UAAU;AACpC,UAAI,UAAU,gBAAgB,WAAW,MAAM,OAAO,CAAC;AAAA,IACzD;AAAO,UAAI,UAAU,gBAAgB,WAAW,aAAa,OAAO,CAAC;AAAA,EACvE;AAGA,QAAM,WAA+B;AAGjC,MAAA;AACA,MAAA,QAAQ,CAAC,IAAI,UAAU,MAAM,MAAM,OAAO,WAAW,YAAY,QAAQ,IAAI;AAC3E,QAAA,UAAU,QAAQ,IAAI;AAAA,EAC5B;AAGA,MAAI,IAAI;AAAO,QAAI,aAAa;AAGhC,MAAI,IAAI,eAAe,OAAO,IAAI,eAAe,KAAK;AACpD,QAAI,aAAa,cAAc;AAC/B,QAAI,aAAa,gBAAgB;AACjC,QAAI,aAAa,mBAAmB;AACvB,iBAAA;AAAA,EACf;AAEI,MAAA,IAAI,WAAW,QAAQ;AACzB,QAAI,IAAI,EAAE;AACH,WAAA;AAAA,EACT;AAEI,MAAA,OAAO,SAAS,UAAU;AAC5B,QAAI,QAAQ,MAAM;AAChB,UAAI,IAAI,EAAE;AACH,aAAA;AAAA,IACE,WAAA,OAAO,SAAS,IAAI,GAAG;AAC5B,UAAA,CAAC,IAAI,UAAU,cAAc;AAAO,YAAA,UAAU,gBAAgB,0BAA0B;AAC5F,UAAI,IAAI,UAAU;AAAA,IACpB;AAAY,WAAA,GAAG,EAAE,YAAY,QAAQ;AAAA,EAAA,OAChC;AACL,QAAI,OAAO,eAAe;AAAU,mBAAa,WAAW;AAExD,QAAA,IAAI,YAAY,QAAQ;AAAA,EAC9B;AAEO,SAAA;AACT;AC9DK,MAAM,aACX,CAAwD,KAAc,QACtE,CAAC,eAAiC;AAChC,QAAM,OAAO,aAAa,UAAU,KAAK,OAAO,UAAU;AAE1D,MAAI,aAAa;AAEb,MAAA,UAAU,gBAAgB,YAAY;AAE1C,SAAO,KAAK,KAAK,GAAG,EAAE,IAAI;AAC5B;ACjBK,MAAM,SACX,CAA6B,QAC7B,CAACA,YAA6B;AAC5B,MAAI,aAAaA;AAEV,SAAA;AACT;ACuBW,MAAA,gBAAgB,CAAC,KAAU,YAA2B;AACjE,MAAI,KAAK,QAAQ,UAAU,QAAQ,kBAAkB,QAAQ,MAAM;AACnE,MAAI,MAAM,QAAQ;AAAiB,UAAA;AAAA,WAC1B,MAAM,QAAQ,WAAW;AAAS,UAAA;AAEvC,MAAA;AAAQ,QAAA,UAAU,iBAAiB,EAAE;AAC3C;AAWa,MAAA,WACX,CAAwD,KAAc,QACtE,CAAC,MAAc,OAAwB,CAAC,GAAG,OAAuC;AAC1E,QAAA,EAAE,MAAM,UAAU,IAAI,WAAW,SAAS,SAAS,GAAG,QAAY,IAAA;AAExE,MAAI,CAAC,WAAW,IAAI,KAAK,CAAC;AAAY,UAAA,IAAI,UAAU,uBAAuB;AAEvE,MAAA;AAAS,kBAAc,KAAK,OAAO;AAEvC,QAAM,WAAW,OAAO,KAAK,MAAM,IAAI,IAAI;AAErC,QAAA,QAAQ,SAAS,QAAQ;AAE/B,UAAQ,kBAAkB,IAAI;AAE9B,UAAQ,eAAe,IAAI,MAAM,MAAM,YAAY;AAEnD,UAAQ,MAAM,IAAI,WAAW,OAAO,QAAQ;AAExC,MAAA,CAAC,IAAI,UAAU,cAAc;AAAG,YAAQ,cAAc,IAAI,KAAK,QAAQ,QAAQ,IAAI,CAAC,IAAI;AAExF,MAAAA,UAAS,IAAI,cAAc;AAE3B,MAAA,IAAI,QAAQ,OAAO,GAAG;AACf,IAAAA,UAAA;AACT,UAAM,CAAC,GAAG,CAAC,IAAI,IAAI,QAAQ,MAAM,QAAQ,UAAU,EAAE,EAAE,MAAM,GAAG;AAC1D,UAAA,MAAO,QAAQ,MAAM,SAAS,GAAG,EAAE,KAAK,MAAM,OAAO;AAC3D,UAAM,QAAS,QAAQ,QAAQ,SAAS,GAAG,EAAE,KAAK;AAElD,QAAI,SAAS,MAAM,QAAQ,OAAO,MAAM,MAAM;AAC5C,UACG,UAAU,KAAK;AAAA,QACd,iBAAiB,WAAW,MAAM,IAAI;AAAA,MAAA,CACvC,EACA,IAAI;AACA,aAAA;AAAA,IACT;AACQ,YAAA,eAAe,IAAI,SAAS,KAAK,IAAI,GAAG,IAAI,MAAM,IAAI;AACtD,YAAA,gBAAgB,IAAI,MAAM,QAAQ;AAC1C,YAAQ,eAAe,IAAI;AAAA,EAAA,OACtB;AACG,YAAA,gBAAgB,IAAI,MAAM;AAAA,EACpC;AAEA,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,OAAO;AAAO,QAAA,UAAU,GAAG,CAAC;AAE5D,MAAA,UAAUA,SAAQ,OAAO;AAEvB,QAAA,SAAS,iBAAiB,UAAU,OAAO;AAE7C,MAAA;AAAI,WAAO,GAAG,SAAS,CAAC,QAAQ,GAAG,GAAG,CAAC,EAAE,GAAG,OAAO,MAAM,GAAI,CAAA;AAEjE,SAAO,KAAK,GAAG;AAER,SAAA;AACT;"}