@tinyhttp/app 2.0.22 → 2.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +279 -340
  2. package/package.json +10 -8
package/dist/index.js CHANGED
@@ -1,364 +1,303 @@
1
- import { STATUS_CODES, createServer } from 'http';
2
- import path from 'path';
3
- import { proxyaddr, all, compile } from '@tinyhttp/proxy-addr';
4
- import { isIP } from 'net';
5
- import { getRequestHeader, getQueryParams, getRangeFromHeader, getAccepts, getAcceptsCharsets, getAcceptsEncodings, getAcceptsLanguages, checkIfXMLHttpRequest, getFreshOrStale, getPathname, getURLParams } from '@tinyhttp/req';
6
- export { getURLParams } from '@tinyhttp/req';
7
- import { Router, pushMiddleware } from '@tinyhttp/router';
8
- import { getResponseHeader, setHeader, send, json, status, sendStatus, sendFile, setContentType, setLocationHeader, setLinksHeader, setVaryHeader, setCookie, clearCookie, formatResponse, redirect, attachment, download, append } from '@tinyhttp/res';
9
- import { parse } from 'regexparam';
10
-
1
+ import { STATUS_CODES, createServer } from "http";
2
+ import path from "path";
3
+ import { proxyaddr, all, compile } from "@tinyhttp/proxy-addr";
4
+ import { isIP } from "net";
5
+ import { getRequestHeader, getQueryParams, getRangeFromHeader, getAccepts, getAcceptsCharsets, getAcceptsEncodings, getAcceptsLanguages, checkIfXMLHttpRequest, getFreshOrStale, getPathname, getURLParams } from "@tinyhttp/req";
6
+ import { getURLParams as getURLParams2 } from "@tinyhttp/req";
7
+ import { Router, pushMiddleware } from "@tinyhttp/router";
8
+ import { getResponseHeader, setHeader, send, json, status, sendStatus, sendFile, setContentType, setLocationHeader, setLinksHeader, setVaryHeader, setCookie, clearCookie, formatResponse, redirect, attachment, download, append } from "@tinyhttp/res";
9
+ import { parse } from "regexparam";
11
10
  const trustRemoteAddress = ({ socket }) => {
12
- const val = socket.remoteAddress;
13
- if (typeof val === 'function')
14
- return val;
15
- if (typeof val === 'boolean' && val === true)
16
- return () => true;
17
- if (typeof val === 'number')
18
- return (_, i) => (val ? i < val : undefined);
19
- if (typeof val === 'string')
20
- return compile(val.split(',').map((x) => x.trim()));
21
- return compile(val || []);
11
+ const val = socket.remoteAddress;
12
+ if (typeof val === "function")
13
+ return val;
14
+ if (typeof val === "boolean" && val === true)
15
+ return () => true;
16
+ if (typeof val === "number")
17
+ return (_, i) => val ? i < val : void 0;
18
+ if (typeof val === "string")
19
+ return compile(val.split(",").map((x) => x.trim()));
20
+ return compile(val || []);
22
21
  };
23
- const getRouteFromApp = ({ middleware }, h) => middleware.find(({ handler }) => typeof handler === 'function' && handler.name === h.name);
22
+ const getRouteFromApp = ({ middleware }, h) => middleware.find(({ handler }) => typeof handler === "function" && handler.name === h.name);
24
23
  const getProtocol = (req) => {
25
- const proto = req.connection.encrypted ? 'https' : 'http';
26
- if (!trustRemoteAddress(req))
27
- return proto;
28
- const header = req.headers['X-Forwarded-Proto'] || proto;
29
- const index = header.indexOf(',');
30
- return index !== -1 ? header.substring(0, index).trim() : header.trim();
24
+ const proto = req.connection.encrypted ? "https" : "http";
25
+ if (!trustRemoteAddress(req))
26
+ return proto;
27
+ const header = req.headers["X-Forwarded-Proto"] || proto;
28
+ const index = header.indexOf(",");
29
+ return index !== -1 ? header.substring(0, index).trim() : header.trim();
31
30
  };
32
31
  const getHostname = (req) => {
33
- let host = req.get('X-Forwarded-Host');
34
- if (!host || !trustRemoteAddress(req))
35
- host = req.get('Host');
36
- if (!host)
37
- return;
38
- // IPv6 literal support
39
- const index = host.indexOf(':', host[0] === '[' ? host.indexOf(']') + 1 : 0);
40
- return index !== -1 ? host.substring(0, index) : host;
32
+ let host = req.get("X-Forwarded-Host");
33
+ if (!host || !trustRemoteAddress(req))
34
+ host = req.get("Host");
35
+ if (!host)
36
+ return;
37
+ const index = host.indexOf(":", host[0] === "[" ? host.indexOf("]") + 1 : 0);
38
+ return index !== -1 ? host.substring(0, index) : host;
41
39
  };
42
- const getIP = (req) => proxyaddr(req, trustRemoteAddress(req)).replace(/^.*:/, ''); // striping the redundant prefix addeded by OS to IPv4 address
40
+ const getIP = (req) => proxyaddr(req, trustRemoteAddress(req)).replace(/^.*:/, "");
43
41
  const getIPs = (req) => all(req, trustRemoteAddress(req));
44
42
  const getSubdomains = (req, subdomainOffset = 2) => {
45
- const hostname = getHostname(req);
46
- if (!hostname)
47
- return [];
48
- const subdomains = isIP(hostname) ? [hostname] : hostname.split('.').reverse();
49
- return subdomains.slice(subdomainOffset);
43
+ const hostname = getHostname(req);
44
+ if (!hostname)
45
+ return [];
46
+ const subdomains = isIP(hostname) ? [hostname] : hostname.split(".").reverse();
47
+ return subdomains.slice(subdomainOffset);
50
48
  };
51
-
52
49
  const onErrorHandler = (err, _req, res) => {
53
- if (!process.env.TESTING && err instanceof Error)
54
- console.error(err);
55
- const code = err.code in STATUS_CODES ? err.code : err.status;
56
- if (typeof err === 'string' || Buffer.isBuffer(err))
57
- res.writeHead(500).end(err);
58
- else if (code in STATUS_CODES)
59
- res.writeHead(code).end(STATUS_CODES[code]);
60
- else
61
- res.writeHead(500).end(err.message);
50
+ if (!process.env.TESTING && err instanceof Error)
51
+ console.error(err);
52
+ const code = err.code in STATUS_CODES ? err.code : err.status;
53
+ if (typeof err === "string" || Buffer.isBuffer(err))
54
+ res.writeHead(500).end(err);
55
+ else if (code in STATUS_CODES)
56
+ res.writeHead(code).end(STATUS_CODES[code]);
57
+ else
58
+ res.writeHead(500).end(err.message);
62
59
  };
63
-
64
60
  const renderTemplate = (_req, res, app) => (file, data, options) => {
65
- app.render(file, data, (err, html) => {
66
- if (err)
67
- throw err;
68
- res.send(html);
69
- }, options);
70
- return res;
61
+ app.render(
62
+ file,
63
+ data ? { ...data, ...res.locals } : res.locals,
64
+ (err, html) => {
65
+ if (err)
66
+ throw err;
67
+ res.send(html);
68
+ },
69
+ options
70
+ );
71
+ return res;
71
72
  };
72
-
73
- /**
74
- * Extends Request and Response objects with custom properties and methods
75
- */
76
73
  const extendMiddleware = (app) => (req, res, next) => {
77
- const { settings } = app;
78
- res.get = getResponseHeader(res);
79
- req.get = getRequestHeader(req);
80
- if (settings === null || settings === void 0 ? void 0 : settings.bindAppToReqRes) {
81
- req.app = app;
82
- res.app = app;
83
- }
84
- if (settings === null || settings === void 0 ? void 0 : settings.networkExtensions) {
85
- req.protocol = getProtocol(req);
86
- req.secure = req.protocol === 'https';
87
- req.connection = Object.assign(req.socket, { encrypted: req.secure });
88
- req.hostname = getHostname(req);
89
- req.subdomains = getSubdomains(req, settings.subdomainOffset);
90
- req.ip = getIP(req);
91
- req.ips = getIPs(req);
92
- }
93
- req.query = getQueryParams(req.url);
94
- req.range = getRangeFromHeader(req);
95
- req.accepts = getAccepts(req);
96
- req.acceptsCharsets = getAcceptsCharsets(req);
97
- req.acceptsEncodings = getAcceptsEncodings(req);
98
- req.acceptsLanguages = getAcceptsLanguages(req);
99
- req.xhr = checkIfXMLHttpRequest(req);
100
- res.header = res.set = setHeader(res);
101
- res.send = send(req, res);
102
- res.json = json(res);
103
- res.status = status(res);
104
- res.sendStatus = sendStatus(req, res);
105
- res.sendFile = sendFile(req, res);
106
- res.type = setContentType(res);
107
- res.location = setLocationHeader(req, res);
108
- res.links = setLinksHeader(res);
109
- res.vary = setVaryHeader(res);
110
- res.cookie = setCookie(req, res);
111
- res.clearCookie = clearCookie(req, res);
112
- res.render = renderTemplate(req, res, app);
113
- res.format = formatResponse(req, res, next);
114
- res.redirect = redirect(req, res, next);
115
- res.attachment = attachment(res);
116
- res.download = download(req, res);
117
- res.append = append(res);
118
- res.locals = res.locals || Object.create(null);
119
- Object.defineProperty(req, 'fresh', { get: getFreshOrStale.bind(null, req, res), configurable: true });
120
- req.stale = !req.fresh;
121
- next();
74
+ const { settings } = app;
75
+ res.get = getResponseHeader(res);
76
+ req.get = getRequestHeader(req);
77
+ if (settings == null ? void 0 : settings.bindAppToReqRes) {
78
+ req.app = app;
79
+ res.app = app;
80
+ }
81
+ if (settings == null ? void 0 : settings.networkExtensions) {
82
+ req.protocol = getProtocol(req);
83
+ req.secure = req.protocol === "https";
84
+ req.connection = Object.assign(req.socket, { encrypted: req.secure });
85
+ req.hostname = getHostname(req);
86
+ req.subdomains = getSubdomains(req, settings.subdomainOffset);
87
+ req.ip = getIP(req);
88
+ req.ips = getIPs(req);
89
+ }
90
+ req.query = getQueryParams(req.url);
91
+ req.range = getRangeFromHeader(req);
92
+ req.accepts = getAccepts(req);
93
+ req.acceptsCharsets = getAcceptsCharsets(req);
94
+ req.acceptsEncodings = getAcceptsEncodings(req);
95
+ req.acceptsLanguages = getAcceptsLanguages(req);
96
+ req.xhr = checkIfXMLHttpRequest(req);
97
+ res.header = res.set = setHeader(res);
98
+ res.send = send(req, res);
99
+ res.json = json(res);
100
+ res.status = status(res);
101
+ res.sendStatus = sendStatus(req, res);
102
+ res.sendFile = sendFile(req, res);
103
+ res.type = setContentType(res);
104
+ res.location = setLocationHeader(req, res);
105
+ res.links = setLinksHeader(res);
106
+ res.vary = setVaryHeader(res);
107
+ res.cookie = setCookie(req, res);
108
+ res.clearCookie = clearCookie(req, res);
109
+ res.render = renderTemplate(req, res, app);
110
+ res.format = formatResponse(req, res, next);
111
+ res.redirect = redirect(req, res, next);
112
+ res.attachment = attachment(res);
113
+ res.download = download(req, res);
114
+ res.append = append(res);
115
+ res.locals = res.locals || /* @__PURE__ */ Object.create(null);
116
+ Object.defineProperty(req, "fresh", { get: getFreshOrStale.bind(null, req, res), configurable: true });
117
+ req.stale = !req.fresh;
118
+ next();
122
119
  };
123
-
124
- /**
125
- * Add leading slash if not present (e.g. path -> /path, /path -> /path)
126
- * @param x
127
- */
128
- const lead = (x) => (x.charCodeAt(0) === 47 ? x : '/' + x);
129
- const mount = (fn) => (fn instanceof App ? fn.attach : fn);
120
+ const lead = (x) => x.charCodeAt(0) === 47 ? x : "/" + x;
121
+ const mount = (fn) => fn instanceof App ? fn.attach : fn;
130
122
  const applyHandler = (h) => async (req, res, next) => {
131
- try {
132
- if (h[Symbol.toStringTag] === 'AsyncFunction') {
133
- await h(req, res, next);
134
- }
135
- else
136
- h(req, res, next);
137
- }
138
- catch (e) {
139
- next(e);
140
- }
123
+ try {
124
+ if (h[Symbol.toStringTag] === "AsyncFunction") {
125
+ await h(req, res, next);
126
+ } else
127
+ h(req, res, next);
128
+ } catch (e) {
129
+ next(e);
130
+ }
141
131
  };
142
- /**
143
- * `App` class - the starting point of tinyhttp app.
144
- *
145
- * With the `App` you can:
146
- * * use routing methods and `.use(...)`
147
- * * set no match (404) and error (500) handlers
148
- * * configure template engines
149
- * * store data in locals
150
- * * listen the http server on a specified port
151
- *
152
- * In case you use TypeScript, you can pass custom types to this class because it is also a generic class.
153
- *
154
- * Example:
155
- *
156
- * ```ts
157
- * interface CoolReq extends Request {
158
- * genericsAreDope: boolean
159
- * }
160
- *
161
- * const app = App<any, CoolReq, Response>()
162
- * ```
163
- */
164
132
  class App extends Router {
165
- constructor(options = {}) {
166
- super();
167
- this.middleware = [];
168
- this.locals = {};
169
- this.engines = {};
170
- this.onError = (options === null || options === void 0 ? void 0 : options.onError) || onErrorHandler;
171
- this.noMatchHandler = (options === null || options === void 0 ? void 0 : options.noMatchHandler) || this.onError.bind(null, { code: 404 });
172
- this.settings = options.settings || { xPoweredBy: true, views: process.cwd() };
173
- this.applyExtensions = options === null || options === void 0 ? void 0 : options.applyExtensions;
174
- this.attach = (req, res) => setImmediate(this.handler.bind(this, req, res, undefined), req, res);
133
+ constructor(options = {}) {
134
+ super();
135
+ this.middleware = [];
136
+ this.locals = {};
137
+ this.engines = {};
138
+ this.onError = (options == null ? void 0 : options.onError) || onErrorHandler;
139
+ this.noMatchHandler = (options == null ? void 0 : options.noMatchHandler) || this.onError.bind(null, { code: 404 });
140
+ this.settings = options.settings || { xPoweredBy: true, views: process.cwd() };
141
+ this.applyExtensions = options == null ? void 0 : options.applyExtensions;
142
+ this.attach = (req, res) => setImmediate(this.handler.bind(this, req, res, void 0), req, res);
143
+ }
144
+ set(setting, value) {
145
+ this.settings[setting] = value;
146
+ return this;
147
+ }
148
+ enable(setting) {
149
+ this.settings[setting] = true;
150
+ return this;
151
+ }
152
+ disable(setting) {
153
+ this.settings[setting] = false;
154
+ return this;
155
+ }
156
+ render(file, data = {}, cb, options = {}) {
157
+ options.viewsFolder = options.viewsFolder || this.settings.views || `${process.cwd()}/views`;
158
+ options.ext = options.ext || file.slice(file.lastIndexOf(".") + 1) || "ejs";
159
+ options._locals = options._locals || {};
160
+ options.cache = options.cache || process.env.NODE_ENV === "production";
161
+ let locals = { ...data, ...this.locals };
162
+ if (options._locals)
163
+ locals = { ...locals, ...options._locals };
164
+ if (!file.endsWith(`.${options.ext}`))
165
+ file = `${file}.${options.ext}`;
166
+ const dest = options.viewsFolder ? path.join(options.viewsFolder, file) : file;
167
+ this.engines[options.ext](dest, locals, options.renderOptions, cb);
168
+ return this;
169
+ }
170
+ use(...args) {
171
+ var _a;
172
+ const base = args[0];
173
+ const fns = args.slice(1).flat();
174
+ if (typeof base === "function" || base instanceof App) {
175
+ fns.unshift(base);
176
+ } else if (Array.isArray(base)) {
177
+ fns.unshift(...base);
175
178
  }
176
- /**
177
- * Set app setting
178
- * @param setting setting name
179
- * @param value setting value
180
- */
181
- set(setting, value) {
182
- this.settings[setting] = value;
183
- return this;
179
+ const path2 = typeof base === "string" ? base : "/";
180
+ let regex;
181
+ for (const fn of fns) {
182
+ if (fn instanceof App) {
183
+ regex = parse(path2, true);
184
+ fn.mountpath = path2;
185
+ this.apps[path2] = fn;
186
+ fn.parent = this;
187
+ }
184
188
  }
185
- /**
186
- * Enable app setting
187
- * @param setting Setting name
188
- */
189
- enable(setting) {
190
- this.settings[setting] = true;
191
- return this;
192
- }
193
- /**
194
- * Disable app setting
195
- * @param setting
196
- */
197
- disable(setting) {
198
- this.settings[setting] = false;
199
- return this;
200
- }
201
- /**
202
- * Render a template
203
- * @param file What to render
204
- * @param data data that is passed to a template
205
- * @param options Template engine options
206
- * @param cb Callback that consumes error and html
207
- */
208
- render(file, data = {}, cb, options = {}) {
209
- options.viewsFolder = options.viewsFolder || this.settings.views || `${process.cwd()}/views`;
210
- options.ext = options.ext || file.slice(file.lastIndexOf('.') + 1) || 'ejs';
211
- options._locals = options._locals || {};
212
- options.cache = options.cache || process.env.NODE_ENV === 'production';
213
- let locals = { ...data, ...this.locals };
214
- if (options._locals)
215
- locals = { ...locals, ...options._locals };
216
- if (!file.endsWith(`.${options.ext}`))
217
- file = `${file}.${options.ext}`;
218
- const dest = options.viewsFolder ? path.join(options.viewsFolder, file) : file;
219
- this.engines[options.ext](dest, locals, options.renderOptions, cb);
220
- return this;
221
- }
222
- use(...args) {
223
- var _a;
224
- const base = args[0];
225
- const fns = args.slice(1).flat();
226
- if (typeof base === 'function' || base instanceof App) {
227
- fns.unshift(base);
228
- }
229
- else if (Array.isArray(base)) {
230
- fns.unshift(...base);
189
+ const handlerPaths = [];
190
+ const handlerFunctions = [];
191
+ const handlerPathBase = path2 === "/" ? "" : lead(path2);
192
+ for (const fn of fns) {
193
+ if (fn instanceof App && ((_a = fn.middleware) == null ? void 0 : _a.length)) {
194
+ for (const mw of fn.middleware) {
195
+ handlerPaths.push(handlerPathBase + lead(mw.path));
196
+ handlerFunctions.push(fn);
231
197
  }
232
- const path = typeof base === 'string' ? base : '/';
233
- let regex;
234
- for (const fn of fns) {
235
- if (fn instanceof App) {
236
- regex = parse(path, true);
237
- fn.mountpath = path;
238
- this.apps[path] = fn;
239
- fn.parent = this;
240
- }
241
- }
242
- const handlerPaths = [];
243
- const handlerFunctions = [];
244
- const handlerPathBase = path === '/' ? '' : lead(path);
245
- for (const fn of fns) {
246
- if (fn instanceof App && ((_a = fn.middleware) === null || _a === void 0 ? void 0 : _a.length)) {
247
- for (const mw of fn.middleware) {
248
- handlerPaths.push(handlerPathBase + lead(mw.path));
249
- handlerFunctions.push(fn);
250
- }
251
- }
252
- else {
253
- handlerPaths.push('');
254
- handlerFunctions.push(fn);
255
- }
256
- }
257
- pushMiddleware(this.middleware)({
258
- path,
259
- regex,
260
- type: 'mw',
261
- handler: mount(handlerFunctions[0]),
262
- handlers: handlerFunctions.slice(1).map(mount),
263
- fullPaths: handlerPaths
264
- });
265
- return this;
266
- }
267
- /**
268
- * Register a template engine with extension
269
- */
270
- engine(ext, fn) {
271
- this.engines[ext] = fn;
272
- return this;
273
- }
274
- route(path) {
275
- const app = new App();
276
- this.use(path, app);
277
- return app;
198
+ } else {
199
+ handlerPaths.push("");
200
+ handlerFunctions.push(fn);
201
+ }
278
202
  }
279
- find(url) {
280
- return this.middleware.filter((m) => {
281
- m.regex = m.regex || parse(m.path, m.type === 'mw');
282
- let fullPathRegex;
283
- m.fullPath && typeof m.fullPath === 'string'
284
- ? (fullPathRegex = parse(m.fullPath, m.type === 'mw'))
285
- : (fullPathRegex = null);
286
- return m.regex.pattern.test(url) && (m.type === 'mw' && fullPathRegex ? fullPathRegex.pattern.test(url) : true);
287
- });
288
- }
289
- /**
290
- * Extends Req / Res objects, pushes 404 and 500 handlers, dispatches middleware
291
- * @param req Req object
292
- * @param res Res object
293
- */
294
- handler(req, res, next) {
295
- /* Set X-Powered-By header */
296
- const { xPoweredBy } = this.settings;
297
- if (xPoweredBy)
298
- res.setHeader('X-Powered-By', typeof xPoweredBy === 'string' ? xPoweredBy : 'tinyhttp');
299
- const exts = this.applyExtensions || extendMiddleware(this);
300
- req.originalUrl = req.url || req.originalUrl;
301
- const pathname = getPathname(req.originalUrl);
302
- const matched = this.find(pathname);
303
- const mw = [
304
- {
305
- handler: exts,
306
- type: 'mw',
307
- path: '/'
308
- },
309
- ...matched.filter((x) => req.method === 'HEAD' || (x.method ? x.method === req.method : true))
310
- ];
311
- if (matched[0] != null) {
312
- mw.push({
313
- type: 'mw',
314
- handler: (req, res, next) => {
315
- if (req.method === 'HEAD') {
316
- res.statusCode = 204;
317
- return res.end('');
318
- }
319
- next();
320
- },
321
- path: '/'
322
- });
323
- }
324
- mw.push({
325
- handler: this.noMatchHandler,
326
- type: 'mw',
327
- path: '/'
328
- });
329
- const handle = (mw) => async (req, res, next) => {
330
- var _a;
331
- const { path, handler, regex } = mw;
332
- const params = regex ? getURLParams(regex, pathname) : {};
333
- req.params = { ...req.params, ...params };
334
- if (path.includes(':')) {
335
- const first = Object.values(params)[0];
336
- const url = req.url.slice(req.url.indexOf(first) + (first === null || first === void 0 ? void 0 : first.length));
337
- req.url = lead(url);
338
- }
339
- else {
340
- req.url = lead(req.url.substring(path.length));
341
- }
342
- if (!req.path)
343
- req.path = getPathname(req.url);
344
- if ((_a = this.settings) === null || _a === void 0 ? void 0 : _a.enableReqRoute)
345
- req.route = getRouteFromApp(this, handler);
346
- await applyHandler(handler)(req, res, next);
347
- };
348
- let idx = 0;
349
- const loop = () => res.writableEnded || (idx < mw.length && handle(mw[idx++])(req, res, next));
350
- next = next || ((err) => (err ? this.onError(err, req, res) : loop()));
351
- loop();
352
- }
353
- /**
354
- * Creates HTTP server and dispatches middleware
355
- * @param port server listening port
356
- * @param Server callback after server starts listening
357
- * @param host server listening host
358
- */
359
- listen(port, cb, host = '0.0.0.0') {
360
- return createServer().on('request', this.attach).listen(port, host, cb);
203
+ pushMiddleware(this.middleware)({
204
+ path: path2,
205
+ regex,
206
+ type: "mw",
207
+ handler: mount(handlerFunctions[0]),
208
+ handlers: handlerFunctions.slice(1).map(mount),
209
+ fullPaths: handlerPaths
210
+ });
211
+ return this;
212
+ }
213
+ engine(ext, fn) {
214
+ this.engines[ext] = fn;
215
+ return this;
216
+ }
217
+ route(path2) {
218
+ const app = new App();
219
+ this.use(path2, app);
220
+ return app;
221
+ }
222
+ find(url) {
223
+ return this.middleware.filter((m) => {
224
+ m.regex = m.regex || parse(m.path, m.type === "mw");
225
+ let fullPathRegex;
226
+ m.fullPath && typeof m.fullPath === "string" ? fullPathRegex = parse(m.fullPath, m.type === "mw") : fullPathRegex = null;
227
+ return m.regex.pattern.test(url) && (m.type === "mw" && fullPathRegex ? fullPathRegex.pattern.test(url) : true);
228
+ });
229
+ }
230
+ handler(req, res, next) {
231
+ const { xPoweredBy } = this.settings;
232
+ if (xPoweredBy)
233
+ res.setHeader("X-Powered-By", typeof xPoweredBy === "string" ? xPoweredBy : "tinyhttp");
234
+ const exts = this.applyExtensions || extendMiddleware(this);
235
+ req.originalUrl = req.url || req.originalUrl;
236
+ const pathname = getPathname(req.originalUrl);
237
+ const matched = this.find(pathname);
238
+ const mw = [
239
+ {
240
+ handler: exts,
241
+ type: "mw",
242
+ path: "/"
243
+ },
244
+ ...matched.filter((x) => req.method === "HEAD" || (x.method ? x.method === req.method : true))
245
+ ];
246
+ if (matched[0] != null) {
247
+ mw.push({
248
+ type: "mw",
249
+ handler: (req2, res2, next2) => {
250
+ if (req2.method === "HEAD") {
251
+ res2.statusCode = 204;
252
+ return res2.end("");
253
+ }
254
+ next2();
255
+ },
256
+ path: "/"
257
+ });
361
258
  }
259
+ mw.push({
260
+ handler: this.noMatchHandler,
261
+ type: "mw",
262
+ path: "/"
263
+ });
264
+ const handle = (mw2) => async (req2, res2, next2) => {
265
+ var _a;
266
+ const { path: path2, handler, regex } = mw2;
267
+ const params = regex ? getURLParams(regex, pathname) : {};
268
+ req2.params = { ...req2.params, ...params };
269
+ if (path2.includes(":")) {
270
+ const first = Object.values(params)[0];
271
+ const url = req2.url.slice(req2.url.indexOf(first) + (first == null ? void 0 : first.length));
272
+ req2.url = lead(url);
273
+ } else {
274
+ req2.url = lead(req2.url.substring(path2.length));
275
+ }
276
+ if (!req2.path)
277
+ req2.path = getPathname(req2.url);
278
+ if ((_a = this.settings) == null ? void 0 : _a.enableReqRoute)
279
+ req2.route = getRouteFromApp(this, handler);
280
+ await applyHandler(handler)(req2, res2, next2);
281
+ };
282
+ let idx = 0;
283
+ const loop = () => res.writableEnded || idx < mw.length && handle(mw[idx++])(req, res, next);
284
+ next = next || ((err) => err ? this.onError(err, req, res) : loop());
285
+ loop();
286
+ }
287
+ listen(port, cb, host = "0.0.0.0") {
288
+ return createServer().on("request", this.attach).listen(port, host, cb);
289
+ }
362
290
  }
363
-
364
- export { App, extendMiddleware, getHostname, getIP, getIPs, getProtocol, getRouteFromApp, getSubdomains, onErrorHandler, renderTemplate };
291
+ export {
292
+ App,
293
+ extendMiddleware,
294
+ getHostname,
295
+ getIP,
296
+ getIPs,
297
+ getProtocol,
298
+ getRouteFromApp,
299
+ getSubdomains,
300
+ getURLParams2 as getURLParams,
301
+ onErrorHandler,
302
+ renderTemplate
303
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinyhttp/app",
3
- "version": "2.0.22",
3
+ "version": "2.0.23",
4
4
  "description": "0-legacy, tiny & fast web framework as a replacement of Express",
5
5
  "type": "module",
6
6
  "homepage": "https://tinyhttp.v1rtl.site",
@@ -32,15 +32,17 @@
32
32
  "author": "v1rtl",
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
- "@tinyhttp/cookie": "2.0.4",
36
- "@tinyhttp/proxy-addr": "2.0.4",
37
- "@tinyhttp/req": "2.0.13",
38
- "@tinyhttp/res": "2.0.17",
39
- "@tinyhttp/router": "2.0.5",
35
+ "@tinyhttp/cookie": "2.0.5",
36
+ "@tinyhttp/proxy-addr": "2.0.5",
37
+ "@tinyhttp/req": "2.0.14",
38
+ "@tinyhttp/res": "2.0.18",
39
+ "@tinyhttp/router": "2.0.6",
40
40
  "header-range-parser": "1.1.3",
41
- "regexparam": "^2.0.0"
41
+ "regexparam": "^2.0.1"
42
42
  },
43
43
  "scripts": {
44
- "build": "rollup -c"
44
+ "dev": "vite",
45
+ "build": "vite build",
46
+ "postbuild": "tsc --emitDeclarationOnly"
45
47
  }
46
48
  }