import { parse, format } from "@tinyhttp/content-type"; import { eTag } from "@tinyhttp/etag"; import { Stats, statSync, createReadStream } from "node:fs"; import { STATUS_CODES } from "node:http"; import { isAbsolute, join, extname } from "node:path"; import mime from "mime"; const json = (res) => (body, ...args) => { res.setHeader("Content-Type", "application/json"); if (typeof body === "object" && body != null) res.end(JSON.stringify(body, null, 2), ...args); else if (typeof body === "string") res.end(body, ...args); else if (body == null) { res.removeHeader("Content-Length"); res.removeHeader("Transfer-Encoding"); res.end(null, ...args); } return res; }; const createETag = (body, encoding) => { if (body instanceof Stats) { return eTag(body, { weak: true }); } else { return eTag(!Buffer.isBuffer(body) ? Buffer.from(body, encoding) : body, { weak: true }); } }; function setCharset(type, charset) { const parsed = parse(type); parsed.parameters.charset = charset; return format(parsed); } const send = (req, res) => (body) => { let bodyToSend = body; if (Buffer.isBuffer(body)) { bodyToSend = body; } else if (typeof body === "object" && body !== null) { bodyToSend = JSON.stringify(body, null, 2); } else if (typeof body === "string") { const type = res.getHeader("Content-Type"); if (type && typeof type === "string") { res.setHeader("Content-Type", setCharset(type, "utf-8")); } else res.setHeader("Content-Type", setCharset("text/html", "utf-8")); } const encoding = "utf8"; let etag; if (body && !res.getHeader("etag") && (etag = createETag(bodyToSend, encoding))) { res.setHeader("etag", etag); } if (req.fresh) res.statusCode = 304; if (res.statusCode === 204 || res.statusCode === 304) { res.removeHeader("Content-Type"); res.removeHeader("Content-Length"); res.removeHeader("Transfer-Encoding"); bodyToSend = ""; } if (req.method === "HEAD") { res.end(""); return res; } if (typeof body === "object") { if (body == null) { res.end(""); return res; } else if (Buffer.isBuffer(body)) { if (!res.getHeader("Content-Type")) res.setHeader("content-type", "application/octet-stream"); res.end(bodyToSend); } else json(res)(bodyToSend, encoding); } else { if (typeof bodyToSend !== "string") bodyToSend = bodyToSend.toString(); res.end(bodyToSend, encoding); } return res; }; const sendStatus = (req, res) => (statusCode) => { const body = STATUS_CODES[statusCode] || String(statusCode); res.statusCode = statusCode; res.setHeader("Content-Type", "text/plain"); return send(req, res)(body); }; const status = (res) => (status2) => { res.statusCode = status2; return res; }; const enableCaching = (res, caching) => { let cc = caching.maxAge != null && `public,max-age=${caching.maxAge}`; if (cc && caching.immutable) cc += ",immutable"; else if (cc && caching.maxAge === 0) cc += ",must-revalidate"; if (cc) res.setHeader("Cache-Control", cc); }; const sendFile = (req, res) => (path, opts = {}, cb) => { const { root, headers = {}, encoding = "utf-8", caching, ...options } = opts; if (!isAbsolute(path) && !root) throw new TypeError("path must be absolute"); if (caching) enableCaching(res, caching); const filePath = root ? join(root, path) : path; const stats = statSync(filePath); headers["Content-Encoding"] = encoding; headers["Last-Modified"] = stats.mtime.toUTCString(); headers["ETag"] = createETag(stats, encoding); if (!res.getHeader("Content-Type")) headers["Content-Type"] = mime.getType(extname(path)) + "; charset=utf-8"; let status2 = res.statusCode || 200; if (req.headers["range"]) { status2 = 206; const [x, y] = req.headers.range.replace("bytes=", "").split("-"); const end = options.end = parseInt(y, 10) || stats.size - 1; const start = options.start = parseInt(x, 10) || 0; if (start >= stats.size || end >= stats.size) { res.writeHead(416, { "Content-Range": `bytes */${stats.size}` }).end(); return res; } headers["Content-Range"] = `bytes ${start}-${end}/${stats.size}`; headers["Content-Length"] = end - start + 1; headers["Accept-Ranges"] = "bytes"; } else { headers["Content-Length"] = stats.size; } for (const [k, v] of Object.entries(headers)) res.setHeader(k, v); res.writeHead(status2, headers); const stream = createReadStream(filePath, options); if (cb) stream.on("error", (err) => cb(err)).on("end", () => cb()); stream.pipe(res); return res; }; export { enableCaching, json, send, sendFile, sendStatus, status }; //# sourceMappingURL=index.js.map