@tinyhttp/app 2.0.22 → 2.0.24
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.
- package/dist/index.js +279 -340
- package/package.json +10 -8
package/dist/index.js
CHANGED
|
@@ -1,364 +1,303 @@
|
|
|
1
|
-
import { STATUS_CODES, createServer } from
|
|
2
|
-
import path from
|
|
3
|
-
import { proxyaddr, all, compile } from
|
|
4
|
-
import { isIP } from
|
|
5
|
-
import { getRequestHeader, getQueryParams, getRangeFromHeader, getAccepts, getAcceptsCharsets, getAcceptsEncodings, getAcceptsLanguages, checkIfXMLHttpRequest, getFreshOrStale, getPathname, getURLParams } from
|
|
6
|
-
|
|
7
|
-
import { Router, pushMiddleware } from
|
|
8
|
-
import { getResponseHeader, setHeader, send, json, status, sendStatus, sendFile, setContentType, setLocationHeader, setLinksHeader, setVaryHeader, setCookie, clearCookie, formatResponse, redirect, attachment, download, append } from
|
|
9
|
-
import { parse } from
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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 ===
|
|
22
|
+
const getRouteFromApp = ({ middleware }, h) => middleware.find(({ handler }) => typeof handler === "function" && handler.name === h.name);
|
|
24
23
|
const getProtocol = (req) => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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(/^.*:/,
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
this.
|
|
183
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
mw
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "2.0.24",
|
|
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.
|
|
36
|
-
"@tinyhttp/proxy-addr": "2.0.
|
|
37
|
-
"@tinyhttp/req": "2.0.
|
|
38
|
-
"@tinyhttp/res": "2.0.
|
|
39
|
-
"@tinyhttp/router": "2.0.
|
|
35
|
+
"@tinyhttp/cookie": "2.0.5",
|
|
36
|
+
"@tinyhttp/proxy-addr": "2.0.5",
|
|
37
|
+
"@tinyhttp/req": "2.0.14",
|
|
38
|
+
"@tinyhttp/res": "2.0.19",
|
|
39
|
+
"@tinyhttp/router": "2.0.6",
|
|
40
40
|
"header-range-parser": "1.1.3",
|
|
41
|
-
"regexparam": "^2.0.
|
|
41
|
+
"regexparam": "^2.0.1"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
|
-
"
|
|
44
|
+
"dev": "vite",
|
|
45
|
+
"build": "vite build",
|
|
46
|
+
"postbuild": "tsc --emitDeclarationOnly"
|
|
45
47
|
}
|
|
46
48
|
}
|