@tinyhttp/app
Advanced tools
Comparing version 2.0.22 to 2.0.23
@@ -1,364 +0,303 @@ | ||
import { STATUS_CODES, createServer } from 'http'; | ||
import path from 'path'; | ||
import { proxyaddr, all, compile } from '@tinyhttp/proxy-addr'; | ||
import { isIP } from 'net'; | ||
import { getRequestHeader, getQueryParams, getRangeFromHeader, getAccepts, getAcceptsCharsets, getAcceptsEncodings, getAcceptsLanguages, checkIfXMLHttpRequest, getFreshOrStale, getPathname, getURLParams } from '@tinyhttp/req'; | ||
export { getURLParams } from '@tinyhttp/req'; | ||
import { Router, pushMiddleware } from '@tinyhttp/router'; | ||
import { getResponseHeader, setHeader, send, json, status, sendStatus, sendFile, setContentType, setLocationHeader, setLinksHeader, setVaryHeader, setCookie, clearCookie, formatResponse, redirect, attachment, download, append } from '@tinyhttp/res'; | ||
import { parse } from 'regexparam'; | ||
import { STATUS_CODES, createServer } from "http"; | ||
import path from "path"; | ||
import { proxyaddr, all, compile } from "@tinyhttp/proxy-addr"; | ||
import { isIP } from "net"; | ||
import { getRequestHeader, getQueryParams, getRangeFromHeader, getAccepts, getAcceptsCharsets, getAcceptsEncodings, getAcceptsLanguages, checkIfXMLHttpRequest, getFreshOrStale, getPathname, getURLParams } from "@tinyhttp/req"; | ||
import { getURLParams as getURLParams2 } from "@tinyhttp/req"; | ||
import { Router, pushMiddleware } from "@tinyhttp/router"; | ||
import { getResponseHeader, setHeader, send, json, status, sendStatus, sendFile, setContentType, setLocationHeader, setLinksHeader, setVaryHeader, setCookie, clearCookie, formatResponse, redirect, attachment, download, append } from "@tinyhttp/res"; | ||
import { parse } from "regexparam"; | ||
const trustRemoteAddress = ({ socket }) => { | ||
const val = socket.remoteAddress; | ||
if (typeof val === 'function') | ||
return val; | ||
if (typeof val === 'boolean' && val === true) | ||
return () => true; | ||
if (typeof val === 'number') | ||
return (_, i) => (val ? i < val : undefined); | ||
if (typeof val === 'string') | ||
return compile(val.split(',').map((x) => x.trim())); | ||
return compile(val || []); | ||
const val = socket.remoteAddress; | ||
if (typeof val === "function") | ||
return val; | ||
if (typeof val === "boolean" && val === true) | ||
return () => true; | ||
if (typeof val === "number") | ||
return (_, i) => val ? i < val : void 0; | ||
if (typeof val === "string") | ||
return compile(val.split(",").map((x) => x.trim())); | ||
return compile(val || []); | ||
}; | ||
const getRouteFromApp = ({ middleware }, h) => middleware.find(({ handler }) => typeof handler === 'function' && handler.name === h.name); | ||
const getRouteFromApp = ({ middleware }, h) => middleware.find(({ handler }) => typeof handler === "function" && handler.name === h.name); | ||
const getProtocol = (req) => { | ||
const proto = req.connection.encrypted ? 'https' : 'http'; | ||
if (!trustRemoteAddress(req)) | ||
return proto; | ||
const header = req.headers['X-Forwarded-Proto'] || proto; | ||
const index = header.indexOf(','); | ||
return index !== -1 ? header.substring(0, index).trim() : header.trim(); | ||
const proto = req.connection.encrypted ? "https" : "http"; | ||
if (!trustRemoteAddress(req)) | ||
return proto; | ||
const header = req.headers["X-Forwarded-Proto"] || proto; | ||
const index = header.indexOf(","); | ||
return index !== -1 ? header.substring(0, index).trim() : header.trim(); | ||
}; | ||
const getHostname = (req) => { | ||
let host = req.get('X-Forwarded-Host'); | ||
if (!host || !trustRemoteAddress(req)) | ||
host = req.get('Host'); | ||
if (!host) | ||
return; | ||
// IPv6 literal support | ||
const index = host.indexOf(':', host[0] === '[' ? host.indexOf(']') + 1 : 0); | ||
return index !== -1 ? host.substring(0, index) : host; | ||
let host = req.get("X-Forwarded-Host"); | ||
if (!host || !trustRemoteAddress(req)) | ||
host = req.get("Host"); | ||
if (!host) | ||
return; | ||
const index = host.indexOf(":", host[0] === "[" ? host.indexOf("]") + 1 : 0); | ||
return index !== -1 ? host.substring(0, index) : host; | ||
}; | ||
const getIP = (req) => proxyaddr(req, trustRemoteAddress(req)).replace(/^.*:/, ''); // striping the redundant prefix addeded by OS to IPv4 address | ||
const getIP = (req) => proxyaddr(req, trustRemoteAddress(req)).replace(/^.*:/, ""); | ||
const getIPs = (req) => all(req, trustRemoteAddress(req)); | ||
const getSubdomains = (req, subdomainOffset = 2) => { | ||
const hostname = getHostname(req); | ||
if (!hostname) | ||
return []; | ||
const subdomains = isIP(hostname) ? [hostname] : hostname.split('.').reverse(); | ||
return subdomains.slice(subdomainOffset); | ||
const hostname = getHostname(req); | ||
if (!hostname) | ||
return []; | ||
const subdomains = isIP(hostname) ? [hostname] : hostname.split(".").reverse(); | ||
return subdomains.slice(subdomainOffset); | ||
}; | ||
const onErrorHandler = (err, _req, res) => { | ||
if (!process.env.TESTING && err instanceof Error) | ||
console.error(err); | ||
const code = err.code in STATUS_CODES ? err.code : err.status; | ||
if (typeof err === 'string' || Buffer.isBuffer(err)) | ||
res.writeHead(500).end(err); | ||
else if (code in STATUS_CODES) | ||
res.writeHead(code).end(STATUS_CODES[code]); | ||
else | ||
res.writeHead(500).end(err.message); | ||
if (!process.env.TESTING && err instanceof Error) | ||
console.error(err); | ||
const code = err.code in STATUS_CODES ? err.code : err.status; | ||
if (typeof err === "string" || Buffer.isBuffer(err)) | ||
res.writeHead(500).end(err); | ||
else if (code in STATUS_CODES) | ||
res.writeHead(code).end(STATUS_CODES[code]); | ||
else | ||
res.writeHead(500).end(err.message); | ||
}; | ||
const renderTemplate = (_req, res, app) => (file, data, options) => { | ||
app.render(file, data, (err, html) => { | ||
if (err) | ||
throw err; | ||
res.send(html); | ||
}, options); | ||
return res; | ||
app.render( | ||
file, | ||
data ? { ...data, ...res.locals } : res.locals, | ||
(err, html) => { | ||
if (err) | ||
throw err; | ||
res.send(html); | ||
}, | ||
options | ||
); | ||
return res; | ||
}; | ||
/** | ||
* Extends Request and Response objects with custom properties and methods | ||
*/ | ||
const extendMiddleware = (app) => (req, res, next) => { | ||
const { settings } = app; | ||
res.get = getResponseHeader(res); | ||
req.get = getRequestHeader(req); | ||
if (settings === null || settings === void 0 ? void 0 : settings.bindAppToReqRes) { | ||
req.app = app; | ||
res.app = app; | ||
} | ||
if (settings === null || settings === void 0 ? void 0 : settings.networkExtensions) { | ||
req.protocol = getProtocol(req); | ||
req.secure = req.protocol === 'https'; | ||
req.connection = Object.assign(req.socket, { encrypted: req.secure }); | ||
req.hostname = getHostname(req); | ||
req.subdomains = getSubdomains(req, settings.subdomainOffset); | ||
req.ip = getIP(req); | ||
req.ips = getIPs(req); | ||
} | ||
req.query = getQueryParams(req.url); | ||
req.range = getRangeFromHeader(req); | ||
req.accepts = getAccepts(req); | ||
req.acceptsCharsets = getAcceptsCharsets(req); | ||
req.acceptsEncodings = getAcceptsEncodings(req); | ||
req.acceptsLanguages = getAcceptsLanguages(req); | ||
req.xhr = checkIfXMLHttpRequest(req); | ||
res.header = res.set = setHeader(res); | ||
res.send = send(req, res); | ||
res.json = json(res); | ||
res.status = status(res); | ||
res.sendStatus = sendStatus(req, res); | ||
res.sendFile = sendFile(req, res); | ||
res.type = setContentType(res); | ||
res.location = setLocationHeader(req, res); | ||
res.links = setLinksHeader(res); | ||
res.vary = setVaryHeader(res); | ||
res.cookie = setCookie(req, res); | ||
res.clearCookie = clearCookie(req, res); | ||
res.render = renderTemplate(req, res, app); | ||
res.format = formatResponse(req, res, next); | ||
res.redirect = redirect(req, res, next); | ||
res.attachment = attachment(res); | ||
res.download = download(req, res); | ||
res.append = append(res); | ||
res.locals = res.locals || Object.create(null); | ||
Object.defineProperty(req, 'fresh', { get: getFreshOrStale.bind(null, req, res), configurable: true }); | ||
req.stale = !req.fresh; | ||
next(); | ||
const { settings } = app; | ||
res.get = getResponseHeader(res); | ||
req.get = getRequestHeader(req); | ||
if (settings == null ? void 0 : settings.bindAppToReqRes) { | ||
req.app = app; | ||
res.app = app; | ||
} | ||
if (settings == null ? void 0 : settings.networkExtensions) { | ||
req.protocol = getProtocol(req); | ||
req.secure = req.protocol === "https"; | ||
req.connection = Object.assign(req.socket, { encrypted: req.secure }); | ||
req.hostname = getHostname(req); | ||
req.subdomains = getSubdomains(req, settings.subdomainOffset); | ||
req.ip = getIP(req); | ||
req.ips = getIPs(req); | ||
} | ||
req.query = getQueryParams(req.url); | ||
req.range = getRangeFromHeader(req); | ||
req.accepts = getAccepts(req); | ||
req.acceptsCharsets = getAcceptsCharsets(req); | ||
req.acceptsEncodings = getAcceptsEncodings(req); | ||
req.acceptsLanguages = getAcceptsLanguages(req); | ||
req.xhr = checkIfXMLHttpRequest(req); | ||
res.header = res.set = setHeader(res); | ||
res.send = send(req, res); | ||
res.json = json(res); | ||
res.status = status(res); | ||
res.sendStatus = sendStatus(req, res); | ||
res.sendFile = sendFile(req, res); | ||
res.type = setContentType(res); | ||
res.location = setLocationHeader(req, res); | ||
res.links = setLinksHeader(res); | ||
res.vary = setVaryHeader(res); | ||
res.cookie = setCookie(req, res); | ||
res.clearCookie = clearCookie(req, res); | ||
res.render = renderTemplate(req, res, app); | ||
res.format = formatResponse(req, res, next); | ||
res.redirect = redirect(req, res, next); | ||
res.attachment = attachment(res); | ||
res.download = download(req, res); | ||
res.append = append(res); | ||
res.locals = res.locals || /* @__PURE__ */ Object.create(null); | ||
Object.defineProperty(req, "fresh", { get: getFreshOrStale.bind(null, req, res), configurable: true }); | ||
req.stale = !req.fresh; | ||
next(); | ||
}; | ||
/** | ||
* Add leading slash if not present (e.g. path -> /path, /path -> /path) | ||
* @param x | ||
*/ | ||
const lead = (x) => (x.charCodeAt(0) === 47 ? x : '/' + x); | ||
const mount = (fn) => (fn instanceof App ? fn.attach : fn); | ||
const lead = (x) => x.charCodeAt(0) === 47 ? x : "/" + x; | ||
const mount = (fn) => fn instanceof App ? fn.attach : fn; | ||
const applyHandler = (h) => async (req, res, next) => { | ||
try { | ||
if (h[Symbol.toStringTag] === 'AsyncFunction') { | ||
await h(req, res, next); | ||
} | ||
else | ||
h(req, res, next); | ||
} | ||
catch (e) { | ||
next(e); | ||
} | ||
try { | ||
if (h[Symbol.toStringTag] === "AsyncFunction") { | ||
await h(req, res, next); | ||
} else | ||
h(req, res, next); | ||
} catch (e) { | ||
next(e); | ||
} | ||
}; | ||
/** | ||
* `App` class - the starting point of tinyhttp app. | ||
* | ||
* With the `App` you can: | ||
* * use routing methods and `.use(...)` | ||
* * set no match (404) and error (500) handlers | ||
* * configure template engines | ||
* * store data in locals | ||
* * listen the http server on a specified port | ||
* | ||
* In case you use TypeScript, you can pass custom types to this class because it is also a generic class. | ||
* | ||
* Example: | ||
* | ||
* ```ts | ||
* interface CoolReq extends Request { | ||
* genericsAreDope: boolean | ||
* } | ||
* | ||
* const app = App<any, CoolReq, Response>() | ||
* ``` | ||
*/ | ||
class App extends Router { | ||
constructor(options = {}) { | ||
super(); | ||
this.middleware = []; | ||
this.locals = {}; | ||
this.engines = {}; | ||
this.onError = (options === null || options === void 0 ? void 0 : options.onError) || onErrorHandler; | ||
this.noMatchHandler = (options === null || options === void 0 ? void 0 : options.noMatchHandler) || this.onError.bind(null, { code: 404 }); | ||
this.settings = options.settings || { xPoweredBy: true, views: process.cwd() }; | ||
this.applyExtensions = options === null || options === void 0 ? void 0 : options.applyExtensions; | ||
this.attach = (req, res) => setImmediate(this.handler.bind(this, req, res, undefined), req, res); | ||
constructor(options = {}) { | ||
super(); | ||
this.middleware = []; | ||
this.locals = {}; | ||
this.engines = {}; | ||
this.onError = (options == null ? void 0 : options.onError) || onErrorHandler; | ||
this.noMatchHandler = (options == null ? void 0 : options.noMatchHandler) || this.onError.bind(null, { code: 404 }); | ||
this.settings = options.settings || { xPoweredBy: true, views: process.cwd() }; | ||
this.applyExtensions = options == null ? void 0 : options.applyExtensions; | ||
this.attach = (req, res) => setImmediate(this.handler.bind(this, req, res, void 0), req, res); | ||
} | ||
set(setting, value) { | ||
this.settings[setting] = value; | ||
return this; | ||
} | ||
enable(setting) { | ||
this.settings[setting] = true; | ||
return this; | ||
} | ||
disable(setting) { | ||
this.settings[setting] = false; | ||
return this; | ||
} | ||
render(file, data = {}, cb, options = {}) { | ||
options.viewsFolder = options.viewsFolder || this.settings.views || `${process.cwd()}/views`; | ||
options.ext = options.ext || file.slice(file.lastIndexOf(".") + 1) || "ejs"; | ||
options._locals = options._locals || {}; | ||
options.cache = options.cache || process.env.NODE_ENV === "production"; | ||
let locals = { ...data, ...this.locals }; | ||
if (options._locals) | ||
locals = { ...locals, ...options._locals }; | ||
if (!file.endsWith(`.${options.ext}`)) | ||
file = `${file}.${options.ext}`; | ||
const dest = options.viewsFolder ? path.join(options.viewsFolder, file) : file; | ||
this.engines[options.ext](dest, locals, options.renderOptions, cb); | ||
return this; | ||
} | ||
use(...args) { | ||
var _a; | ||
const base = args[0]; | ||
const fns = args.slice(1).flat(); | ||
if (typeof base === "function" || base instanceof App) { | ||
fns.unshift(base); | ||
} else if (Array.isArray(base)) { | ||
fns.unshift(...base); | ||
} | ||
/** | ||
* Set app setting | ||
* @param setting setting name | ||
* @param value setting value | ||
*/ | ||
set(setting, value) { | ||
this.settings[setting] = value; | ||
return this; | ||
const path2 = typeof base === "string" ? base : "/"; | ||
let regex; | ||
for (const fn of fns) { | ||
if (fn instanceof App) { | ||
regex = parse(path2, true); | ||
fn.mountpath = path2; | ||
this.apps[path2] = fn; | ||
fn.parent = this; | ||
} | ||
} | ||
/** | ||
* Enable app setting | ||
* @param setting Setting name | ||
*/ | ||
enable(setting) { | ||
this.settings[setting] = true; | ||
return this; | ||
} | ||
/** | ||
* Disable app setting | ||
* @param setting | ||
*/ | ||
disable(setting) { | ||
this.settings[setting] = false; | ||
return this; | ||
} | ||
/** | ||
* Render a template | ||
* @param file What to render | ||
* @param data data that is passed to a template | ||
* @param options Template engine options | ||
* @param cb Callback that consumes error and html | ||
*/ | ||
render(file, data = {}, cb, options = {}) { | ||
options.viewsFolder = options.viewsFolder || this.settings.views || `${process.cwd()}/views`; | ||
options.ext = options.ext || file.slice(file.lastIndexOf('.') + 1) || 'ejs'; | ||
options._locals = options._locals || {}; | ||
options.cache = options.cache || process.env.NODE_ENV === 'production'; | ||
let locals = { ...data, ...this.locals }; | ||
if (options._locals) | ||
locals = { ...locals, ...options._locals }; | ||
if (!file.endsWith(`.${options.ext}`)) | ||
file = `${file}.${options.ext}`; | ||
const dest = options.viewsFolder ? path.join(options.viewsFolder, file) : file; | ||
this.engines[options.ext](dest, locals, options.renderOptions, cb); | ||
return this; | ||
} | ||
use(...args) { | ||
var _a; | ||
const base = args[0]; | ||
const fns = args.slice(1).flat(); | ||
if (typeof base === 'function' || base instanceof App) { | ||
fns.unshift(base); | ||
const handlerPaths = []; | ||
const handlerFunctions = []; | ||
const handlerPathBase = path2 === "/" ? "" : lead(path2); | ||
for (const fn of fns) { | ||
if (fn instanceof App && ((_a = fn.middleware) == null ? void 0 : _a.length)) { | ||
for (const mw of fn.middleware) { | ||
handlerPaths.push(handlerPathBase + lead(mw.path)); | ||
handlerFunctions.push(fn); | ||
} | ||
else if (Array.isArray(base)) { | ||
fns.unshift(...base); | ||
} | ||
const path = typeof base === 'string' ? base : '/'; | ||
let regex; | ||
for (const fn of fns) { | ||
if (fn instanceof App) { | ||
regex = parse(path, true); | ||
fn.mountpath = path; | ||
this.apps[path] = fn; | ||
fn.parent = this; | ||
} | ||
} | ||
const handlerPaths = []; | ||
const handlerFunctions = []; | ||
const handlerPathBase = path === '/' ? '' : lead(path); | ||
for (const fn of fns) { | ||
if (fn instanceof App && ((_a = fn.middleware) === null || _a === void 0 ? void 0 : _a.length)) { | ||
for (const mw of fn.middleware) { | ||
handlerPaths.push(handlerPathBase + lead(mw.path)); | ||
handlerFunctions.push(fn); | ||
} | ||
} | ||
else { | ||
handlerPaths.push(''); | ||
handlerFunctions.push(fn); | ||
} | ||
} | ||
pushMiddleware(this.middleware)({ | ||
path, | ||
regex, | ||
type: 'mw', | ||
handler: mount(handlerFunctions[0]), | ||
handlers: handlerFunctions.slice(1).map(mount), | ||
fullPaths: handlerPaths | ||
}); | ||
return this; | ||
} else { | ||
handlerPaths.push(""); | ||
handlerFunctions.push(fn); | ||
} | ||
} | ||
/** | ||
* Register a template engine with extension | ||
*/ | ||
engine(ext, fn) { | ||
this.engines[ext] = fn; | ||
return this; | ||
pushMiddleware(this.middleware)({ | ||
path: path2, | ||
regex, | ||
type: "mw", | ||
handler: mount(handlerFunctions[0]), | ||
handlers: handlerFunctions.slice(1).map(mount), | ||
fullPaths: handlerPaths | ||
}); | ||
return this; | ||
} | ||
engine(ext, fn) { | ||
this.engines[ext] = fn; | ||
return this; | ||
} | ||
route(path2) { | ||
const app = new App(); | ||
this.use(path2, app); | ||
return app; | ||
} | ||
find(url) { | ||
return this.middleware.filter((m) => { | ||
m.regex = m.regex || parse(m.path, m.type === "mw"); | ||
let fullPathRegex; | ||
m.fullPath && typeof m.fullPath === "string" ? fullPathRegex = parse(m.fullPath, m.type === "mw") : fullPathRegex = null; | ||
return m.regex.pattern.test(url) && (m.type === "mw" && fullPathRegex ? fullPathRegex.pattern.test(url) : true); | ||
}); | ||
} | ||
handler(req, res, next) { | ||
const { xPoweredBy } = this.settings; | ||
if (xPoweredBy) | ||
res.setHeader("X-Powered-By", typeof xPoweredBy === "string" ? xPoweredBy : "tinyhttp"); | ||
const exts = this.applyExtensions || extendMiddleware(this); | ||
req.originalUrl = req.url || req.originalUrl; | ||
const pathname = getPathname(req.originalUrl); | ||
const matched = this.find(pathname); | ||
const mw = [ | ||
{ | ||
handler: exts, | ||
type: "mw", | ||
path: "/" | ||
}, | ||
...matched.filter((x) => req.method === "HEAD" || (x.method ? x.method === req.method : true)) | ||
]; | ||
if (matched[0] != null) { | ||
mw.push({ | ||
type: "mw", | ||
handler: (req2, res2, next2) => { | ||
if (req2.method === "HEAD") { | ||
res2.statusCode = 204; | ||
return res2.end(""); | ||
} | ||
next2(); | ||
}, | ||
path: "/" | ||
}); | ||
} | ||
route(path) { | ||
const app = new App(); | ||
this.use(path, app); | ||
return app; | ||
} | ||
find(url) { | ||
return this.middleware.filter((m) => { | ||
m.regex = m.regex || parse(m.path, m.type === 'mw'); | ||
let fullPathRegex; | ||
m.fullPath && typeof m.fullPath === 'string' | ||
? (fullPathRegex = parse(m.fullPath, m.type === 'mw')) | ||
: (fullPathRegex = null); | ||
return m.regex.pattern.test(url) && (m.type === 'mw' && fullPathRegex ? fullPathRegex.pattern.test(url) : true); | ||
}); | ||
} | ||
/** | ||
* Extends Req / Res objects, pushes 404 and 500 handlers, dispatches middleware | ||
* @param req Req object | ||
* @param res Res object | ||
*/ | ||
handler(req, res, next) { | ||
/* Set X-Powered-By header */ | ||
const { xPoweredBy } = this.settings; | ||
if (xPoweredBy) | ||
res.setHeader('X-Powered-By', typeof xPoweredBy === 'string' ? xPoweredBy : 'tinyhttp'); | ||
const exts = this.applyExtensions || extendMiddleware(this); | ||
req.originalUrl = req.url || req.originalUrl; | ||
const pathname = getPathname(req.originalUrl); | ||
const matched = this.find(pathname); | ||
const mw = [ | ||
{ | ||
handler: exts, | ||
type: 'mw', | ||
path: '/' | ||
}, | ||
...matched.filter((x) => req.method === 'HEAD' || (x.method ? x.method === req.method : true)) | ||
]; | ||
if (matched[0] != null) { | ||
mw.push({ | ||
type: 'mw', | ||
handler: (req, res, next) => { | ||
if (req.method === 'HEAD') { | ||
res.statusCode = 204; | ||
return res.end(''); | ||
} | ||
next(); | ||
}, | ||
path: '/' | ||
}); | ||
} | ||
mw.push({ | ||
handler: this.noMatchHandler, | ||
type: 'mw', | ||
path: '/' | ||
}); | ||
const handle = (mw) => async (req, res, next) => { | ||
var _a; | ||
const { path, handler, regex } = mw; | ||
const params = regex ? getURLParams(regex, pathname) : {}; | ||
req.params = { ...req.params, ...params }; | ||
if (path.includes(':')) { | ||
const first = Object.values(params)[0]; | ||
const url = req.url.slice(req.url.indexOf(first) + (first === null || first === void 0 ? void 0 : first.length)); | ||
req.url = lead(url); | ||
} | ||
else { | ||
req.url = lead(req.url.substring(path.length)); | ||
} | ||
if (!req.path) | ||
req.path = getPathname(req.url); | ||
if ((_a = this.settings) === null || _a === void 0 ? void 0 : _a.enableReqRoute) | ||
req.route = getRouteFromApp(this, handler); | ||
await applyHandler(handler)(req, res, next); | ||
}; | ||
let idx = 0; | ||
const loop = () => res.writableEnded || (idx < mw.length && handle(mw[idx++])(req, res, next)); | ||
next = next || ((err) => (err ? this.onError(err, req, res) : loop())); | ||
loop(); | ||
} | ||
/** | ||
* Creates HTTP server and dispatches middleware | ||
* @param port server listening port | ||
* @param Server callback after server starts listening | ||
* @param host server listening host | ||
*/ | ||
listen(port, cb, host = '0.0.0.0') { | ||
return createServer().on('request', this.attach).listen(port, host, cb); | ||
} | ||
mw.push({ | ||
handler: this.noMatchHandler, | ||
type: "mw", | ||
path: "/" | ||
}); | ||
const handle = (mw2) => async (req2, res2, next2) => { | ||
var _a; | ||
const { path: path2, handler, regex } = mw2; | ||
const params = regex ? getURLParams(regex, pathname) : {}; | ||
req2.params = { ...req2.params, ...params }; | ||
if (path2.includes(":")) { | ||
const first = Object.values(params)[0]; | ||
const url = req2.url.slice(req2.url.indexOf(first) + (first == null ? void 0 : first.length)); | ||
req2.url = lead(url); | ||
} else { | ||
req2.url = lead(req2.url.substring(path2.length)); | ||
} | ||
if (!req2.path) | ||
req2.path = getPathname(req2.url); | ||
if ((_a = this.settings) == null ? void 0 : _a.enableReqRoute) | ||
req2.route = getRouteFromApp(this, handler); | ||
await applyHandler(handler)(req2, res2, next2); | ||
}; | ||
let idx = 0; | ||
const loop = () => res.writableEnded || idx < mw.length && handle(mw[idx++])(req, res, next); | ||
next = next || ((err) => err ? this.onError(err, req, res) : loop()); | ||
loop(); | ||
} | ||
listen(port, cb, host = "0.0.0.0") { | ||
return createServer().on("request", this.attach).listen(port, host, cb); | ||
} | ||
} | ||
export { App, extendMiddleware, getHostname, getIP, getIPs, getProtocol, getRouteFromApp, getSubdomains, onErrorHandler, renderTemplate }; | ||
export { | ||
App, | ||
extendMiddleware, | ||
getHostname, | ||
getIP, | ||
getIPs, | ||
getProtocol, | ||
getRouteFromApp, | ||
getSubdomains, | ||
getURLParams2 as getURLParams, | ||
onErrorHandler, | ||
renderTemplate | ||
}; |
{ | ||
"name": "@tinyhttp/app", | ||
"version": "2.0.22", | ||
"version": "2.0.23", | ||
"description": "0-legacy, tiny & fast web framework as a replacement of Express", | ||
@@ -35,13 +35,15 @@ "type": "module", | ||
"dependencies": { | ||
"@tinyhttp/cookie": "2.0.4", | ||
"@tinyhttp/proxy-addr": "2.0.4", | ||
"@tinyhttp/req": "2.0.13", | ||
"@tinyhttp/res": "2.0.17", | ||
"@tinyhttp/router": "2.0.5", | ||
"@tinyhttp/cookie": "2.0.5", | ||
"@tinyhttp/proxy-addr": "2.0.5", | ||
"@tinyhttp/req": "2.0.14", | ||
"@tinyhttp/res": "2.0.18", | ||
"@tinyhttp/router": "2.0.6", | ||
"header-range-parser": "1.1.3", | ||
"regexparam": "^2.0.0" | ||
"regexparam": "^2.0.1" | ||
}, | ||
"scripts": { | ||
"build": "rollup -c" | ||
"dev": "vite", | ||
"build": "vite build", | ||
"postbuild": "tsc --emitDeclarationOnly" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
23480
546
+ Added@tinyhttp/accepts@2.0.7(transitive)
+ Added@tinyhttp/content-disposition@2.0.7(transitive)
+ Added@tinyhttp/cookie@2.0.5(transitive)
+ Added@tinyhttp/cookie-signature@2.0.5(transitive)
+ Added@tinyhttp/encode-url@2.0.6(transitive)
+ Added@tinyhttp/etag@2.0.5(transitive)
+ Added@tinyhttp/forwarded@2.0.5(transitive)
+ Added@tinyhttp/proxy-addr@2.0.5(transitive)
+ Added@tinyhttp/req@2.0.14(transitive)
+ Added@tinyhttp/res@2.0.18(transitive)
+ Added@tinyhttp/router@2.0.6(transitive)
+ Added@tinyhttp/send@2.0.6(transitive)
+ Added@tinyhttp/type-is@2.0.6(transitive)
+ Added@tinyhttp/url@2.0.7(transitive)
- Removed@tinyhttp/accepts@2.0.6(transitive)
- Removed@tinyhttp/content-disposition@2.0.6(transitive)
- Removed@tinyhttp/cookie@2.0.4(transitive)
- Removed@tinyhttp/cookie-signature@2.0.4(transitive)
- Removed@tinyhttp/encode-url@2.0.5(transitive)
- Removed@tinyhttp/etag@2.0.4(transitive)
- Removed@tinyhttp/forwarded@2.0.4(transitive)
- Removed@tinyhttp/proxy-addr@2.0.4(transitive)
- Removed@tinyhttp/req@2.0.13(transitive)
- Removed@tinyhttp/res@2.0.17(transitive)
- Removed@tinyhttp/router@2.0.5(transitive)
- Removed@tinyhttp/send@2.0.5(transitive)
- Removed@tinyhttp/type-is@2.0.5(transitive)
- Removed@tinyhttp/url@2.0.6(transitive)
Updated@tinyhttp/cookie@2.0.5
Updated@tinyhttp/proxy-addr@2.0.5
Updated@tinyhttp/req@2.0.14
Updated@tinyhttp/res@2.0.18
Updated@tinyhttp/router@2.0.6
Updatedregexparam@^2.0.1