UNPKG

@uploadx/core

Version:
158 lines (157 loc) 5.37 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.typeis = void 0; exports.readBody = readBody; exports.getJsonBody = getJsonBody; exports.getHeader = getHeader; exports.appendHeader = appendHeader; exports.setHeaders = setHeaders; exports.getBaseUrl = getBaseUrl; exports.extractHost = extractHost; exports.extractProto = extractProto; exports.responseToTuple = responseToTuple; exports.tupleToResponse = tupleToResponse; exports.normalizeHookResponse = normalizeHookResponse; exports.normalizeOnErrorResponse = normalizeOnErrorResponse; const primitives_1 = require("./primitives"); const typeis = (req, types) => { const contentType = req.headers['content-type'] || ''; return exports.typeis.is(contentType, types); }; exports.typeis = typeis; exports.typeis.is = (mime, types = ['/']) => { const re = new RegExp(types.map(str => str.replace(/[*+]/g, '')).join('|')); return mime.replace(/[*+]/g, '').search(re) !== -1 ? mime : false; }; exports.typeis.hasBody = (req) => { const bodySize = Number(req.headers['content-length']); return !isNaN(bodySize) && bodySize; }; /** * Reads the body from the request. * @param req - request object * @param encoding - encoding to use * @param limit - optional limit on the size of the body */ function readBody(req, encoding = 'utf8', limit = Infinity) { return new Promise((resolve, reject) => { let body = ''; req.setEncoding(encoding); req.on('data', chunk => { body += chunk; if (Buffer.byteLength(body) > limit) return reject('Content length mismatch'); }); req.once('end', () => resolve(body)); }); } /** * Reads the body of the incoming metadata request and parses it as JSON. * @param req - incoming metadata request * @param limit - optional limit on the size of the body */ async function getJsonBody(req, limit = 16777216) { if ((0, exports.typeis)(req, ['json'])) { if (req.body) return { ...req.body }; const contentLength = exports.typeis.hasBody(req); if (contentLength) { if (contentLength > limit) return Promise.reject('Content length limit exceeded'); const raw = await readBody(req, 'utf8', contentLength); return { ...JSON.parse(raw) }; } } return {}; } /** * Retrieve the value of a specific header of an HTTP request. * @param req - request object * @param name - name of the header * @param all - if true, returns all values of the header, comma-separated, otherwise returns the last value. */ function getHeader(req, name, all = false) { const raw = req.headers?.[name.toLowerCase()]; if (!raw || raw.length === 0) return ''; return all ? raw.toString().trim() : (0, primitives_1.getLastOne)(Array.isArray(raw) ? raw : raw.split(',')).trim(); } /** * Appends value to the end of the multi-value header */ function appendHeader(res, name, value) { const s = [res.getHeader(name), value].flat().filter(Boolean).toString(); res.setHeader(name, s); } /** * Sets the value of a specific header of an HTTP response. */ function setHeaders(res, headers = {}) { const keys = Object.keys(headers); keys.length && appendHeader(res, 'Access-Control-Expose-Headers', keys); for (const key of keys) { ['location', 'link'].includes(key.toLowerCase()) ? res.setHeader(key, encodeURI(headers[key].toString())) : res.setHeader(key, headers[key]); } } /** * Try build a protocol:hostname:port string from a request object. */ function getBaseUrl(req) { const host = extractHost(req); if (!host) return ''; const proto = extractProto(req); if (!proto) return `//${host}`; return `${proto}://${host}`; } /** * Extracts host with port from a http or https request. */ function extractHost(req) { return getHeader(req, 'host'); // return req.host || req.hostname || getHeader(req, 'host'); // for express v5 / fastify } /** * Extracts protocol from a http or https request. */ function extractProto(req) { return getHeader(req, 'x-forwarded-proto').toLowerCase(); } function responseToTuple(response) { if (Array.isArray(response)) return response; const { statusCode = 200, headers, ...rest } = response; const body = response.body ? response.body : rest; return [statusCode, body, headers || {}]; } function tupleToResponse(response) { if (!Array.isArray(response)) return response; const [statusCode, body, headers] = response; return { statusCode, body, headers }; } function normalizeHookResponse(fn) { return async (file) => { const response = await fn(file); if ((0, primitives_1.isRecord)(response)) { const { statusCode, headers, body, ...rest } = response; return { statusCode, headers, body: body ?? rest }; } return { body: response ?? '' }; }; } /* @internal */ function normalizeOnErrorResponse(fn) { return (error) => { if ((0, primitives_1.isRecord)(error)) { const { statusCode, headers, body, ...rest } = error; return fn({ statusCode, headers, body: body ?? rest }); } return fn({ body: error ?? 'unknown error', statusCode: 500 }); }; }