slower 2.1.7 → 2.1.9
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/package.json +15 -15
- package/src/decorators.js +65 -60
- package/src/slower.js +166 -131
- package/src/utils.js +32 -30
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
2
|
+
"name": "slower",
|
|
3
|
+
"version": "2.1.9",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"devDependencies": {},
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"path-to-regexp": "^6.2.2"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [],
|
|
13
|
+
"author": "Tomás Luchesi <no.mad.devtech@gmail.com>",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"description": "A package for simple HTTP server routing."
|
|
16
|
+
}
|
package/src/decorators.js
CHANGED
|
@@ -4,14 +4,14 @@ const utils = require('./utils');
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Receives and configures the HTTP Response object
|
|
7
|
-
* @param {http.ServerResponse} response
|
|
8
|
-
* @returns {http.ServerResponse}
|
|
7
|
+
* @param {http.ServerResponse} response
|
|
8
|
+
* @returns {http.ServerResponse}
|
|
9
9
|
* @exposes .status()
|
|
10
10
|
* @exposes .send()
|
|
11
11
|
* @exposes .json()
|
|
12
12
|
* @exposes .file()
|
|
13
13
|
*/
|
|
14
|
-
function setupResponse
|
|
14
|
+
function setupResponse(response, request) {
|
|
15
15
|
/**
|
|
16
16
|
* Sets the response status code
|
|
17
17
|
* @chainable
|
|
@@ -27,10 +27,10 @@ function setupResponse (response) {
|
|
|
27
27
|
* Sets a response header to a specified value
|
|
28
28
|
* @chainable
|
|
29
29
|
* @overload
|
|
30
|
-
* @param {string} header
|
|
31
|
-
* @param {string} value
|
|
30
|
+
* @param {string} header
|
|
31
|
+
* @param {string} value
|
|
32
32
|
* @returns {http.ServerResponse}
|
|
33
|
-
* @info Pass in an object of "header":"value" entries
|
|
33
|
+
* @info Pass in an object of "header":"value" entries
|
|
34
34
|
* to set multiple headers at once
|
|
35
35
|
* @example
|
|
36
36
|
* res.set('Content-Length', 1000)
|
|
@@ -45,22 +45,19 @@ function setupResponse (response) {
|
|
|
45
45
|
* res.set({ 'Content-Length':1000, 'Content-Type':'text/plain' })
|
|
46
46
|
*/
|
|
47
47
|
response.set = function (header, value) {
|
|
48
|
-
if (typeof header === 'string')
|
|
49
|
-
|
|
50
|
-
else
|
|
51
|
-
for (let prop of header)
|
|
52
|
-
response.setHeader(prop, header[prop]);
|
|
48
|
+
if (typeof header === 'string') response.setHeader(header, value);
|
|
49
|
+
else for (let prop of header) response.setHeader(prop, header[prop]);
|
|
53
50
|
return response;
|
|
54
|
-
}
|
|
51
|
+
};
|
|
55
52
|
|
|
56
53
|
/**
|
|
57
54
|
* An alias for 'res.getHeader()'.
|
|
58
|
-
* @param {string} header
|
|
55
|
+
* @param {string} header
|
|
59
56
|
* @returns {string}
|
|
60
57
|
*/
|
|
61
|
-
response.get = function(header) {
|
|
58
|
+
response.get = function (header) {
|
|
62
59
|
return response.getHeader(header);
|
|
63
|
-
}
|
|
60
|
+
};
|
|
64
61
|
|
|
65
62
|
/**
|
|
66
63
|
* Sets the Content-Type header with a specific MIME type.
|
|
@@ -70,32 +67,32 @@ function setupResponse (response) {
|
|
|
70
67
|
* @info And if no type is specified, binary type is used (application/octet-stream)
|
|
71
68
|
*/
|
|
72
69
|
response.type = function (mime) {
|
|
73
|
-
let mimetype = MIME_TABLE[mime] || mime || MIME_TABLE[
|
|
70
|
+
let mimetype = MIME_TABLE[mime] || mime || MIME_TABLE['default'];
|
|
74
71
|
response.setHeader('Content-type', mimetype);
|
|
75
72
|
return response;
|
|
76
|
-
}
|
|
73
|
+
};
|
|
77
74
|
|
|
78
75
|
/**
|
|
79
76
|
* Sends a string or buffer as JSON and ends the response
|
|
80
|
-
* @param {string|Buffer} data
|
|
77
|
+
* @param {string|Buffer} data
|
|
81
78
|
* @returns {undefined}
|
|
82
79
|
*/
|
|
83
|
-
response.json = function json
|
|
80
|
+
response.json = function json(data) {
|
|
84
81
|
if (response.statusCode === undefined) response.status(200);
|
|
85
82
|
response.setHeader('Content-type', 'application/json');
|
|
86
83
|
response.write(JSON.stringify(data));
|
|
87
84
|
response.end();
|
|
88
85
|
return undefined;
|
|
89
|
-
}
|
|
86
|
+
};
|
|
90
87
|
|
|
91
88
|
/**
|
|
92
89
|
* Sends a string or buffer as HTML data and ends the response
|
|
93
|
-
* @param {string|Buffer} data
|
|
90
|
+
* @param {string|Buffer} data
|
|
94
91
|
* @returns {undefined}
|
|
95
92
|
*/
|
|
96
93
|
response.send = function (data) {
|
|
97
94
|
if (response.statusCode === undefined) response.status(200);
|
|
98
|
-
if (!response.getHeader('Content-Type'))
|
|
95
|
+
if (!response.getHeader('Content-Type'))
|
|
99
96
|
response.setHeader('Content-Type', 'text/html');
|
|
100
97
|
response.write(data);
|
|
101
98
|
response.end();
|
|
@@ -110,41 +107,46 @@ function setupResponse (response) {
|
|
|
110
107
|
response.file = function (filename) {
|
|
111
108
|
if (response.statusCode === undefined) response.status(200);
|
|
112
109
|
if (!response.getHeader('Content-Type')) {
|
|
113
|
-
let extension = (filename||'').split('.').slice(-1)[0];
|
|
114
|
-
response.setHeader(
|
|
110
|
+
let extension = (filename || '').split('.').slice(-1)[0];
|
|
111
|
+
response.setHeader(
|
|
112
|
+
'Content-Type',
|
|
113
|
+
MIME_TABLE[extension] || MIME_TABLE['default']
|
|
114
|
+
);
|
|
115
115
|
}
|
|
116
116
|
const stream = fs.createReadStream(filename);
|
|
117
117
|
stream.pipe(response);
|
|
118
118
|
return undefined;
|
|
119
|
-
}
|
|
119
|
+
};
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
122
|
+
* Renders an HTML document and sends it to client
|
|
123
|
+
* @param {string} filename The file to use as rendering View
|
|
124
|
+
* @param {object} locals The properties to replace in the View
|
|
125
|
+
* @info The locals object may contain either strings or functions or objects with 'toString' method.
|
|
126
|
+
* @returns {undefined}
|
|
127
|
+
* @example
|
|
128
|
+
* // In 'home.html:
|
|
129
|
+
* <h2>{{user}}</h2> has <h2>{{count}}</h2> items
|
|
130
|
+
* // In server.js: (generates a new random item for each request)
|
|
131
|
+
* res.render('./views/home.html', { user: 'Mike', count: () => randomInt() });
|
|
132
|
+
*/
|
|
133
133
|
response.render = function (filename, locals = {}) {
|
|
134
134
|
let html = fs.readFileSync(filename, 'utf-8');
|
|
135
135
|
for (let item in locals) {
|
|
136
136
|
html = html.replace(
|
|
137
|
-
new RegExp('{{'+item+'}}', 'gim'),
|
|
138
|
-
typeof locals[item] === 'function'
|
|
139
|
-
|
|
137
|
+
new RegExp('{{' + item + '}}', 'gim'),
|
|
138
|
+
typeof locals[item] === 'function'
|
|
139
|
+
? locals[item]()
|
|
140
|
+
: locals[item]?.toString() || ''
|
|
140
141
|
);
|
|
141
142
|
}
|
|
142
|
-
response.
|
|
143
|
+
if (!response.getHeader('Content-Type'))
|
|
144
|
+
response.setHeader('Content-type', 'text/html');
|
|
143
145
|
response.write(html);
|
|
144
146
|
response.end();
|
|
145
147
|
return undefined;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
+
};
|
|
149
|
+
|
|
148
150
|
/**
|
|
149
151
|
* Sets the 'Location' header and redirects to a given path, URL, or link.
|
|
150
152
|
* @param {string} location An URL, link, path, or the string 'back' to set the target
|
|
@@ -160,50 +162,53 @@ function setupResponse (response) {
|
|
|
160
162
|
* res.status(300).redirect('http://example.com');
|
|
161
163
|
*/
|
|
162
164
|
response.redirect = function (location) {
|
|
163
|
-
response.setHeader(
|
|
165
|
+
response.setHeader(
|
|
166
|
+
'Location',
|
|
167
|
+
location === 'back'
|
|
168
|
+
? request.getHeader('Referrer')
|
|
169
|
+
: location || '/'
|
|
170
|
+
);
|
|
164
171
|
response.end();
|
|
165
172
|
return undefined;
|
|
166
|
-
}
|
|
167
|
-
|
|
173
|
+
};
|
|
168
174
|
|
|
169
175
|
return response;
|
|
170
176
|
}
|
|
171
177
|
|
|
172
178
|
/**
|
|
173
179
|
* Receives and configures the HTTP Request object
|
|
174
|
-
* @param {http.IncomingMessage} request
|
|
180
|
+
* @param {http.IncomingMessage} request
|
|
175
181
|
* @returns {http.IncomingMessage}
|
|
176
182
|
* @exposes req.body
|
|
177
183
|
* @exposes req.urlParts
|
|
178
184
|
* @exposes req.query
|
|
179
185
|
* @exposes req.session = { port, rport, host, rhost }
|
|
180
186
|
*/
|
|
181
|
-
function setupRequest
|
|
187
|
+
function setupRequest(request) {
|
|
182
188
|
/**
|
|
183
189
|
* @property
|
|
184
190
|
* Holds the request body data as a buffer
|
|
185
191
|
*/
|
|
186
|
-
return new Promise
|
|
192
|
+
return new Promise((resolve, reject) => {
|
|
187
193
|
// Set classical socket locals
|
|
188
194
|
request.session = {
|
|
189
195
|
port: request.socket.localPort,
|
|
190
196
|
rport: request.socket.remotePort,
|
|
191
197
|
host: utils.normalizeAddress(request.socket.localAddress),
|
|
192
|
-
rhost: utils.normalizeAddress(request.socket.remoteAddress)
|
|
198
|
+
rhost: utils.normalizeAddress(request.socket.remoteAddress),
|
|
193
199
|
};
|
|
194
|
-
|
|
195
|
-
// Set express-compatible locals
|
|
196
|
-
request.ip = request.session.rhost;
|
|
197
200
|
|
|
198
|
-
|
|
201
|
+
// Set express-compatible locals
|
|
202
|
+
request.ip = request.session.rhost;
|
|
203
|
+
|
|
199
204
|
/**
|
|
200
205
|
* An alias for 'req.getHeader()'.
|
|
201
|
-
* @param {string} header
|
|
206
|
+
* @param {string} header
|
|
202
207
|
* @returns {string}
|
|
203
208
|
*/
|
|
204
|
-
request.get = function(header) {
|
|
209
|
+
request.get = function (header) {
|
|
205
210
|
return request.getHeader(header);
|
|
206
|
-
}
|
|
211
|
+
};
|
|
207
212
|
|
|
208
213
|
// Add req.params placeholder, it is added in the main router, not here
|
|
209
214
|
request.params = undefined;
|
|
@@ -215,17 +220,17 @@ function setupRequest (request) {
|
|
|
215
220
|
request.body = [];
|
|
216
221
|
request.on('timeout', () => reject(new Error('Timeout')));
|
|
217
222
|
request.on('data', chunk => request.body.push(chunk));
|
|
218
|
-
request.on('error',
|
|
223
|
+
request.on('error', err => reject(err));
|
|
219
224
|
request.on('end', () => {
|
|
220
225
|
let tmp = Buffer.concat(request.body);
|
|
221
226
|
request.body = {
|
|
222
227
|
buffer: tmp,
|
|
223
228
|
text: () => tmp.toString(),
|
|
224
|
-
json: () => JSON.parse(tmp)
|
|
225
|
-
}
|
|
229
|
+
json: () => JSON.parse(tmp),
|
|
230
|
+
};
|
|
226
231
|
return resolve(request);
|
|
227
232
|
});
|
|
228
233
|
});
|
|
229
234
|
}
|
|
230
235
|
|
|
231
|
-
module.exports = { setupRequest, setupResponse };
|
|
236
|
+
module.exports = { setupRequest, setupResponse };
|
package/src/slower.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
const http = require('node:http');
|
|
3
2
|
const https = require('node:https');
|
|
4
3
|
const path = require('node:path');
|
|
@@ -20,16 +19,16 @@ class SlowerRouter {
|
|
|
20
19
|
|
|
21
20
|
// You can create it with HTTPS options here
|
|
22
21
|
/**
|
|
23
|
-
* Use HTTPS server instead of HTTP.
|
|
22
|
+
* Use HTTPS server instead of HTTP.
|
|
24
23
|
* Pass in all regular HTTPS options as parameters.
|
|
25
24
|
* 'key' and 'cert' options are required for HTTPS.
|
|
26
|
-
* @param {object} options
|
|
25
|
+
* @param {object} options
|
|
27
26
|
* @returns {http.Server|https.Server}
|
|
28
27
|
* @example
|
|
29
28
|
* SlowerRouter({https:true, key:'...', cert:'...'}); // Create HTTPS server
|
|
30
29
|
* SlowerRouter(); // Create regular HTTP
|
|
31
30
|
*/
|
|
32
|
-
constructor
|
|
31
|
+
constructor(options = {}) {
|
|
33
32
|
this.METHODS = HTTP_VERBS;
|
|
34
33
|
this.layers = new Map();
|
|
35
34
|
|
|
@@ -42,7 +41,7 @@ class SlowerRouter {
|
|
|
42
41
|
* or
|
|
43
42
|
* app.get({handler}) -> apply handlers for any route for a specific method
|
|
44
43
|
* app.get({handler}, {handler}, ...) -> apply handlers for any route for a specific method
|
|
45
|
-
*
|
|
44
|
+
*
|
|
46
45
|
*/
|
|
47
46
|
for (let verb of HTTP_VERBS) {
|
|
48
47
|
this.layers.set(verb, new Map());
|
|
@@ -55,22 +54,24 @@ class SlowerRouter {
|
|
|
55
54
|
};
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
if (options.https)
|
|
59
|
-
|
|
60
|
-
else
|
|
61
|
-
this.#server = http.createServer(options);
|
|
57
|
+
if (options.https) this.#server = https.createServer(options);
|
|
58
|
+
else this.#server = http.createServer(options);
|
|
62
59
|
|
|
63
60
|
this.#server.on('request', this.#requestHandlerWrapper(this));
|
|
64
61
|
}
|
|
65
|
-
|
|
66
|
-
#requestHandlerWrapper
|
|
62
|
+
|
|
63
|
+
#requestHandlerWrapper() {
|
|
67
64
|
// Save the 'this' scope
|
|
68
65
|
// Inside the requestHandler function, 'this' corresponds to the http.Server instance
|
|
69
66
|
const self = this;
|
|
70
67
|
|
|
71
|
-
return
|
|
68
|
+
return async function requestHandler(req, res) {
|
|
72
69
|
// Get all routes that match the URL and join with middlewares to cycle
|
|
73
|
-
let foundRoutes = utils.getMatchingRoute(
|
|
70
|
+
let foundRoutes = utils.getMatchingRoute(
|
|
71
|
+
req.url,
|
|
72
|
+
req.method,
|
|
73
|
+
self.layers
|
|
74
|
+
);
|
|
74
75
|
let layers = foundRoutes;
|
|
75
76
|
// Add an Error:404 special layer to the end of the layers list
|
|
76
77
|
// This prevents accidental requests hanging
|
|
@@ -78,33 +79,46 @@ class SlowerRouter {
|
|
|
78
79
|
|
|
79
80
|
// Set properties on request and response objects
|
|
80
81
|
req = await setupRequest(req);
|
|
81
|
-
res = await setupResponse(res);
|
|
82
|
+
res = await setupResponse(res, req);
|
|
82
83
|
|
|
83
84
|
// Cycle throught all middlewares and proper routes and call with 'next()' as third argument
|
|
84
|
-
|
|
85
|
+
(async function cycleMatching(routes) {
|
|
85
86
|
if (routes.length === 0) return;
|
|
86
87
|
let route = routes[0];
|
|
87
88
|
if (route.params) req.params = route.params;
|
|
88
89
|
if (route.callback) route = route.callback;
|
|
89
90
|
route(req, res, async () => cycleMatching(routes.slice(1)));
|
|
90
91
|
})(layers);
|
|
91
|
-
}
|
|
92
|
+
};
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
listen
|
|
95
|
-
|
|
95
|
+
listen(...v) {
|
|
96
|
+
return this.#server.listen(...v);
|
|
97
|
+
}
|
|
98
|
+
close(callback) {
|
|
99
|
+
return this.#server.close(callback);
|
|
100
|
+
}
|
|
96
101
|
|
|
97
102
|
// Add any type of route
|
|
98
|
-
#setRoute
|
|
99
|
-
if (typeof method !== 'string')
|
|
100
|
-
throw new Error(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
#setRoute(method, path, handler) {
|
|
104
|
+
if (typeof method !== 'string')
|
|
105
|
+
throw new Error(
|
|
106
|
+
'<SlowerRouter>.route :: "method" parameter must be of type String'
|
|
107
|
+
);
|
|
108
|
+
if (
|
|
109
|
+
typeof path !== 'string' &&
|
|
110
|
+
typeof path !== 'function' &&
|
|
111
|
+
path?.constructor?.name !== 'RegExp'
|
|
112
|
+
)
|
|
113
|
+
throw new Error(
|
|
114
|
+
'<SlowerRouter>.route :: "path" parameter must be of type Function, String or RegExp'
|
|
115
|
+
);
|
|
103
116
|
if (typeof handler !== 'function')
|
|
104
|
-
throw new Error(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (
|
|
117
|
+
throw new Error(
|
|
118
|
+
'<SlowerRouter>.route :: "handler" parameter must be of type Function'
|
|
119
|
+
);
|
|
120
|
+
if (!this.layers.get(method)) this.layers.set(method, new Map());
|
|
121
|
+
if (typeof path === 'string' || path?.constructor?.name !== 'RegExp')
|
|
108
122
|
path = match(path, { decode: decodeURIComponent }); // 'path' is a function now
|
|
109
123
|
this.layers.get(method).set(path, handler);
|
|
110
124
|
return this;
|
|
@@ -114,7 +128,7 @@ class SlowerRouter {
|
|
|
114
128
|
/**
|
|
115
129
|
* Create a middleware for all HTTP methods, for a specific path
|
|
116
130
|
* @overload
|
|
117
|
-
* @param {String} path
|
|
131
|
+
* @param {String} path
|
|
118
132
|
* @param {...Function} handlers
|
|
119
133
|
* @returns {SlowerRouter}
|
|
120
134
|
* @example Applies a middleware for all methods, for a specific path
|
|
@@ -123,7 +137,7 @@ class SlowerRouter {
|
|
|
123
137
|
/**
|
|
124
138
|
* Create a global middleware for a specific verb (all paths for that method)
|
|
125
139
|
* @info This is the same as using "app[method]({callback})", without a path. Like: app.get(() => {});
|
|
126
|
-
* @overload
|
|
140
|
+
* @overload
|
|
127
141
|
* @param {string} verb (one of the HTTP verbs)
|
|
128
142
|
* @param {...Function} handlers
|
|
129
143
|
* @returns {SlowerRouter}
|
|
@@ -132,44 +146,44 @@ class SlowerRouter {
|
|
|
132
146
|
*/
|
|
133
147
|
/**
|
|
134
148
|
* Create a global middleware (all paths and all HTTP methods)
|
|
135
|
-
* @overload
|
|
136
|
-
* @param {...Function} handlers
|
|
149
|
+
* @overload
|
|
150
|
+
* @param {...Function} handlers
|
|
137
151
|
* @returns {SlowerRouter}
|
|
138
152
|
* @example Applies a middleware for all HTTP requests (of any method)
|
|
139
153
|
* app.all((req, res, next) => {console.log(...); next()})
|
|
140
154
|
*/
|
|
141
|
-
all
|
|
142
|
-
|
|
155
|
+
all(path, ...handlers) {
|
|
143
156
|
// function signature: app.all({METHOD}, ...{HANDLERS})
|
|
144
157
|
// example: app.all('post', () => {});
|
|
145
158
|
if (typeof path === 'string' && HTTP_VERBS.includes(path.toLowerCase()))
|
|
146
159
|
for (let handler of handlers)
|
|
147
160
|
this.#setRoute(path, MATCH_ANY, handler);
|
|
148
|
-
|
|
149
|
-
|
|
150
161
|
// function signature: app.all({PATH}, ...{HANDLERS})
|
|
151
162
|
// example: app.all('/api', () => {});
|
|
152
163
|
else if (typeof path === 'string')
|
|
153
164
|
for (let handler of handlers)
|
|
154
|
-
for (let verb of HTTP_VERBS)
|
|
165
|
+
for (let verb of HTTP_VERBS)
|
|
155
166
|
this.#setRoute(verb, path, handler);
|
|
156
|
-
|
|
157
167
|
// function signature: app.all(...{HANDLERS})
|
|
158
|
-
// example: app.all(() => {});
|
|
168
|
+
// example: app.all(() => {});
|
|
159
169
|
else if (typeof path === 'function')
|
|
160
170
|
for (let verb of HTTP_VERBS) {
|
|
161
171
|
this.#setRoute(verb, MATCH_ANY, path);
|
|
162
172
|
for (let handler of handlers)
|
|
163
173
|
this.#setRoute(verb, MATCH_ANY, handler);
|
|
164
174
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
175
|
+
else
|
|
176
|
+
throw new Error(
|
|
177
|
+
'<SlowerRouter>.use :: "handler" parameter must be of type Function'
|
|
178
|
+
);
|
|
168
179
|
|
|
169
180
|
return this;
|
|
170
181
|
}
|
|
171
182
|
// Just a more comprehensive call to app.all for defining middlewares
|
|
172
|
-
use
|
|
183
|
+
use(...b) {
|
|
184
|
+
this.all(...b);
|
|
185
|
+
return this;
|
|
186
|
+
}
|
|
173
187
|
|
|
174
188
|
/**
|
|
175
189
|
* Serve static files from a directory
|
|
@@ -179,33 +193,46 @@ class SlowerRouter {
|
|
|
179
193
|
* @example
|
|
180
194
|
* // Using a mounting poing:
|
|
181
195
|
* app.static('./public', '/files') // Access with 'GET /files/{filename}'
|
|
182
|
-
*
|
|
196
|
+
*
|
|
183
197
|
* // Not using a mounting point:
|
|
184
198
|
* app.static('./public') // Access with 'GET /public/{filename}'
|
|
185
|
-
*
|
|
199
|
+
*
|
|
186
200
|
* // Using root ('/') as mounting point:
|
|
187
201
|
* app.static('./public', '/') // Access with 'GET /{filename}'
|
|
188
202
|
*/
|
|
189
|
-
static
|
|
203
|
+
static(directoryPath, mountPath = '') {
|
|
190
204
|
const absoluteDir = path.resolve(directoryPath);
|
|
191
205
|
if (!fs.existsSync(absoluteDir))
|
|
192
|
-
throw new Error(
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Invalid directory provided for [SlowerRouter].static(): [${directoryPath}]`
|
|
208
|
+
);
|
|
193
209
|
|
|
194
210
|
for (const file of utils.getFiles(absoluteDir)) {
|
|
195
211
|
// Get only the file name from the absolute file path 'c:\u\a.txt' -> 'a.txt'
|
|
196
|
-
let fileAsURLPath = file
|
|
212
|
+
let fileAsURLPath = file
|
|
213
|
+
.replaceAll(/\\/g, '/')
|
|
214
|
+
.split('/')
|
|
215
|
+
.slice(-1)[0];
|
|
197
216
|
// If there is a mount path, merge it with the file and remove duplicate bars '//'
|
|
198
|
-
if (!mountPath)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
217
|
+
if (!mountPath)
|
|
218
|
+
mountPath = directoryPath
|
|
219
|
+
.replaceAll(/\\/g, '/')
|
|
220
|
+
.split('/')
|
|
221
|
+
.slice(-1)[0];
|
|
222
|
+
fileAsURLPath = ('/' + mountPath + '/' + fileAsURLPath).replaceAll(
|
|
223
|
+
/\/+/g,
|
|
224
|
+
'/'
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
async function staticfhandle(req, res /*next*/) {
|
|
203
228
|
try {
|
|
204
229
|
const fileStream = createReadStream(file);
|
|
205
|
-
res.setHeader(
|
|
230
|
+
res.setHeader(
|
|
231
|
+
'Content-Type',
|
|
232
|
+
MIME_TABLE[extension] || MIME_TABLE['default']
|
|
233
|
+
);
|
|
206
234
|
return await pipeline(fileStream, res);
|
|
207
|
-
|
|
208
|
-
} catch (err) {
|
|
235
|
+
} catch {
|
|
209
236
|
// In case the file does not exist, return a 404 and prevent it from breaking
|
|
210
237
|
res.status(404).end();
|
|
211
238
|
}
|
|
@@ -216,7 +243,7 @@ class SlowerRouter {
|
|
|
216
243
|
// If the file is an html file, also add a path for using it without the '.html' extension
|
|
217
244
|
// both 'GET /files/somefile.html' and 'GET /files/somefile' would work
|
|
218
245
|
const extension = utils.getFileExtension(file);
|
|
219
|
-
if (extension === 'html')
|
|
246
|
+
if (extension === 'html')
|
|
220
247
|
this.get(fileAsURLPath.replace('.html', ''), staticfhandle);
|
|
221
248
|
}
|
|
222
249
|
return this;
|
|
@@ -225,13 +252,14 @@ class SlowerRouter {
|
|
|
225
252
|
/**
|
|
226
253
|
* Import and use another SlowerRouter, mounted at a specific path
|
|
227
254
|
* @param {string} mountPath Mount the router at a specific path
|
|
228
|
-
* @param {SlowerRouter} SlowerRouterInstance
|
|
255
|
+
* @param {SlowerRouter} SlowerRouterInstance
|
|
229
256
|
*/
|
|
230
|
-
useRouter
|
|
231
|
-
const mount = p =>
|
|
257
|
+
useRouter(SlowerSubRouterInstance, mountPath = '/') {
|
|
258
|
+
const mount = p =>
|
|
259
|
+
mountPath ? (mountPath + '/' + p).replace(/\/{2,}/g, '/') : p;
|
|
232
260
|
const layers = SlowerSubRouterInstance.layers;
|
|
233
261
|
for (let layer of layers) {
|
|
234
|
-
if (
|
|
262
|
+
if (layer.static) {
|
|
235
263
|
this.static(layer.directoryPath, mount(layer.mountPath));
|
|
236
264
|
continue;
|
|
237
265
|
}
|
|
@@ -243,38 +271,36 @@ class SlowerRouter {
|
|
|
243
271
|
/**
|
|
244
272
|
* Creates a temporary synced mini-router for a specific path
|
|
245
273
|
* Allows to declare multiple handlers for methods of a specific path
|
|
246
|
-
* @param {string} path
|
|
274
|
+
* @param {string} path
|
|
247
275
|
* @returns {SlowerMicroRouter}
|
|
248
|
-
* @example
|
|
276
|
+
* @example
|
|
249
277
|
* app.route('/books')
|
|
250
278
|
* .get((req, res) => console.log('you retrieved a book with GET'))
|
|
251
279
|
* .post((req, res) => console.log('you added a book with POST'))
|
|
252
280
|
* ;
|
|
253
281
|
*/
|
|
254
|
-
route
|
|
282
|
+
route(path) {
|
|
255
283
|
return new SlowerMicroRouter(path, this.layers);
|
|
256
284
|
}
|
|
257
285
|
}
|
|
258
286
|
|
|
259
|
-
|
|
260
287
|
/**
|
|
261
288
|
* Used as the class for modular routers
|
|
262
289
|
* used in "slower.Router()"
|
|
263
290
|
*/
|
|
264
291
|
class SlowerSubRouter {
|
|
265
|
-
constructor
|
|
292
|
+
constructor() {
|
|
266
293
|
this.METHODS = HTTP_VERBS;
|
|
267
294
|
this.layers = new Array();
|
|
268
295
|
|
|
269
296
|
// OLD version: (Do not delete until new version is properly tested)
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
297
|
+
// Create basic route shortcuts
|
|
298
|
+
// get(), post(), put(), delete()
|
|
299
|
+
// for (let verb of HTTP_VERBS) {
|
|
300
|
+
// this[verb] = function (path, callback) {
|
|
301
|
+
// return this.#setRoute(verb, path, callback);
|
|
302
|
+
// };
|
|
303
|
+
// }
|
|
278
304
|
|
|
279
305
|
// Create basic route shortcuts
|
|
280
306
|
// get(), post(), put(), delete(), ...
|
|
@@ -285,7 +311,7 @@ class SlowerSubRouter {
|
|
|
285
311
|
* or
|
|
286
312
|
* app.get({handler}) -> apply handlers for any route for a specific method
|
|
287
313
|
* app.get({handler}, {handler}, ...) -> apply handlers for any route for a specific method
|
|
288
|
-
*
|
|
314
|
+
*
|
|
289
315
|
*/
|
|
290
316
|
for (let verb of HTTP_VERBS) {
|
|
291
317
|
this[verb] = function (path, ...callbacks) {
|
|
@@ -298,13 +324,19 @@ class SlowerSubRouter {
|
|
|
298
324
|
}
|
|
299
325
|
}
|
|
300
326
|
|
|
301
|
-
#setRoute
|
|
302
|
-
if (typeof method !== 'string')
|
|
303
|
-
throw new Error(
|
|
327
|
+
#setRoute(method, path, handler) {
|
|
328
|
+
if (typeof method !== 'string')
|
|
329
|
+
throw new Error(
|
|
330
|
+
'<SlowerSubRouter>.route :: "method" parameter must be of type String'
|
|
331
|
+
);
|
|
304
332
|
if (typeof path !== 'string' && path?.constructor?.name !== 'RegExp')
|
|
305
|
-
throw new Error(
|
|
333
|
+
throw new Error(
|
|
334
|
+
'<SlowerSubRouter>.route :: "path" parameter must be of type Function, String or RegExp'
|
|
335
|
+
);
|
|
306
336
|
if (typeof handler !== 'function')
|
|
307
|
-
throw new Error(
|
|
337
|
+
throw new Error(
|
|
338
|
+
'<SlowerSubRouter>.route :: "handler" parameter must be of type Function'
|
|
339
|
+
);
|
|
308
340
|
this.layers.push({ method, path, handler });
|
|
309
341
|
return this;
|
|
310
342
|
}
|
|
@@ -313,8 +345,8 @@ class SlowerSubRouter {
|
|
|
313
345
|
/**
|
|
314
346
|
* Create a middleware for all HTTP methods, for a specific path
|
|
315
347
|
* @overload
|
|
316
|
-
* @param {String} path
|
|
317
|
-
* @param {...Function} handlers
|
|
348
|
+
* @param {String} path
|
|
349
|
+
* @param {...Function} handlers
|
|
318
350
|
* @returns {SlowerRouter}
|
|
319
351
|
* @example Applies a middleware for all methods, for a specific path
|
|
320
352
|
* app.all('/img/:id', (req, res, next) => {console.log(...); next()})
|
|
@@ -322,7 +354,7 @@ class SlowerSubRouter {
|
|
|
322
354
|
/**
|
|
323
355
|
* Create a global middleware for a specific verb (all paths for that method)
|
|
324
356
|
* @info This is the same as using "app[method]({callback})", without a path. Like: app.get(() => {});
|
|
325
|
-
* @overload
|
|
357
|
+
* @overload
|
|
326
358
|
* @param {string} verb (one of the HTTP verbs)
|
|
327
359
|
* @param {...Function} handlers
|
|
328
360
|
* @returns {SlowerRouter}
|
|
@@ -331,46 +363,46 @@ class SlowerSubRouter {
|
|
|
331
363
|
*/
|
|
332
364
|
/**
|
|
333
365
|
* Create a global middleware (all paths and all HTTP methods)
|
|
334
|
-
* @overload
|
|
366
|
+
* @overload
|
|
335
367
|
* @param {...Function} handlers
|
|
336
368
|
* @returns {SlowerRouter}
|
|
337
369
|
* @example Applies a middleware for all HTTP requests (of any method)
|
|
338
370
|
* app.all((req, res, next) => {console.log(...); next()})
|
|
339
371
|
*/
|
|
340
|
-
all
|
|
341
|
-
|
|
372
|
+
all(path, ...handlers) {
|
|
342
373
|
// function signature: app.all({METHOD}, ...{HANDLERS})
|
|
343
374
|
// example: app.all('post', () => {});
|
|
344
375
|
if (typeof path === 'string' && HTTP_VERBS.includes(path.toLowerCase()))
|
|
345
376
|
for (let handler of handlers)
|
|
346
377
|
this.#setRoute(path, MATCH_ANY, handler);
|
|
347
|
-
|
|
348
|
-
|
|
349
378
|
// function signature: app.all({PATH}, ...{HANDLERS})
|
|
350
379
|
// example: app.all('/api', () => {});
|
|
351
380
|
else if (typeof path === 'string')
|
|
352
381
|
for (let handler of handlers)
|
|
353
|
-
for (let verb of HTTP_VERBS)
|
|
382
|
+
for (let verb of HTTP_VERBS)
|
|
354
383
|
this.#setRoute(verb, path, handler);
|
|
355
|
-
|
|
356
384
|
// function signature: app.all(...{HANDLERS})
|
|
357
|
-
// example: app.all(() => {});
|
|
385
|
+
// example: app.all(() => {});
|
|
358
386
|
else if (typeof path === 'function')
|
|
359
387
|
for (let verb of HTTP_VERBS) {
|
|
360
388
|
this.#setRoute(verb, MATCH_ANY, path);
|
|
361
389
|
for (let handler of handlers)
|
|
362
390
|
this.#setRoute(verb, MATCH_ANY, handler);
|
|
363
391
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
392
|
+
else
|
|
393
|
+
throw new Error(
|
|
394
|
+
'<SlowerSubRouter>.use :: "handler" parameter must be of type Function'
|
|
395
|
+
);
|
|
367
396
|
|
|
368
397
|
return this;
|
|
369
398
|
}
|
|
370
399
|
// Just a more comprehensive call to app.all for defining middlewares
|
|
371
|
-
use
|
|
400
|
+
use(...b) {
|
|
401
|
+
this.all(...b);
|
|
402
|
+
return this;
|
|
403
|
+
}
|
|
372
404
|
|
|
373
|
-
static
|
|
405
|
+
static(directoryPath, mountPath = '') {
|
|
374
406
|
this.layers.push({ static: true, directoryPath, mountPath });
|
|
375
407
|
return this;
|
|
376
408
|
}
|
|
@@ -378,31 +410,29 @@ class SlowerSubRouter {
|
|
|
378
410
|
/**
|
|
379
411
|
* Creates a temporary synced mini-router for a specific path
|
|
380
412
|
* Allows to declare multiple handlers for methods of a specific path
|
|
381
|
-
* @param {string} path
|
|
413
|
+
* @param {string} path
|
|
382
414
|
* @returns {SlowerMicroRouter}
|
|
383
|
-
* @example
|
|
415
|
+
* @example
|
|
384
416
|
* app.route('/books')
|
|
385
417
|
* .get((req, res) => console.log('you retrieved a book with GET'))
|
|
386
418
|
* .post((req, res) => console.log('you added a book with POST'))
|
|
387
419
|
* ;
|
|
388
420
|
*/
|
|
389
|
-
route
|
|
421
|
+
route(path) {
|
|
390
422
|
return new SlowerMicroRouter(path, this.layers);
|
|
391
423
|
}
|
|
392
424
|
}
|
|
393
425
|
|
|
394
|
-
|
|
395
|
-
|
|
396
426
|
/**
|
|
397
427
|
* Used as the class for micro-routing with "app.route"
|
|
398
|
-
*
|
|
428
|
+
*
|
|
399
429
|
* only method acessors (.get(), .post(), .put(), ...)
|
|
400
430
|
* and .all() and .use()
|
|
401
|
-
*
|
|
431
|
+
*
|
|
402
432
|
* .static() is not allowed
|
|
403
433
|
*/
|
|
404
434
|
class SlowerMicroRouter {
|
|
405
|
-
constructor
|
|
435
|
+
constructor(path, layerPool) {
|
|
406
436
|
this.path = path;
|
|
407
437
|
this.layers = layerPool;
|
|
408
438
|
|
|
@@ -410,21 +440,26 @@ class SlowerMicroRouter {
|
|
|
410
440
|
// get(), post(), put(), delete()
|
|
411
441
|
for (let verb of HTTP_VERBS) {
|
|
412
442
|
this[verb] = function (...callbacks) {
|
|
413
|
-
for (let callback of callbacks)
|
|
414
|
-
|
|
415
|
-
return this;
|
|
443
|
+
for (let callback of callbacks) this.#setRoute(verb, callback);
|
|
444
|
+
return this;
|
|
416
445
|
};
|
|
417
446
|
}
|
|
418
447
|
}
|
|
419
448
|
|
|
420
|
-
#setRoute
|
|
449
|
+
#setRoute(method, handler) {
|
|
421
450
|
const path = this.path;
|
|
422
|
-
if (typeof method !== 'string')
|
|
423
|
-
throw new Error(
|
|
451
|
+
if (typeof method !== 'string')
|
|
452
|
+
throw new Error(
|
|
453
|
+
'<SlowerMicroRouter>.route :: "method" parameter must be of type String'
|
|
454
|
+
);
|
|
424
455
|
if (typeof path !== 'string' && path?.constructor?.name !== 'RegExp')
|
|
425
|
-
throw new Error(
|
|
456
|
+
throw new Error(
|
|
457
|
+
'<SlowerMicroRouter>.route :: "path" parameter must be of type Function, String or RegExp'
|
|
458
|
+
);
|
|
426
459
|
if (typeof handler !== 'function')
|
|
427
|
-
throw new Error(
|
|
460
|
+
throw new Error(
|
|
461
|
+
'<SlowerMicroRouter>.route :: "handler" parameter must be of type Function'
|
|
462
|
+
);
|
|
428
463
|
this.layers.push({ method, path, handler });
|
|
429
464
|
return this;
|
|
430
465
|
}
|
|
@@ -433,8 +468,8 @@ class SlowerMicroRouter {
|
|
|
433
468
|
/**
|
|
434
469
|
* Create a middleware for all HTTP methods, for a specific path
|
|
435
470
|
* @overload
|
|
436
|
-
* @param {String} path
|
|
437
|
-
* @param {...Function} handlers
|
|
471
|
+
* @param {String} path
|
|
472
|
+
* @param {...Function} handlers
|
|
438
473
|
* @returns {SlowerRouter}
|
|
439
474
|
* @example Applies a middleware for all methods, for a specific path
|
|
440
475
|
* app.all('/img/:id', (req, res, next) => {console.log(...); next()})
|
|
@@ -442,63 +477,63 @@ class SlowerMicroRouter {
|
|
|
442
477
|
/**
|
|
443
478
|
* Create a global middleware for a specific verb (all paths for that method)
|
|
444
479
|
* @info This is the same as using "app[method]({callback})", without a path. Like: app.get(() => {});
|
|
445
|
-
* @overload
|
|
480
|
+
* @overload
|
|
446
481
|
* @param {string} verb (one of the HTTP verbs)
|
|
447
|
-
* @param {...Function} handlers
|
|
482
|
+
* @param {...Function} handlers
|
|
448
483
|
* @returns {SlowerRouter}
|
|
449
484
|
* @example Applies a middleware for all GET requests
|
|
450
485
|
* app.all('get', (req, res, next) => {console.log(...); next()})
|
|
451
486
|
*/
|
|
452
487
|
/**
|
|
453
488
|
* Create a global middleware (all paths and all HTTP methods)
|
|
454
|
-
* @overload
|
|
455
|
-
* @param {...Function} handlers
|
|
489
|
+
* @overload
|
|
490
|
+
* @param {...Function} handlers
|
|
456
491
|
* @returns {SlowerRouter}
|
|
457
492
|
* @example Applies a middleware for all HTTP requests (of any method)
|
|
458
493
|
* app.all((req, res, next) => {console.log(...); next()})
|
|
459
494
|
*/
|
|
460
|
-
all
|
|
461
|
-
|
|
495
|
+
all(path, ...handlers) {
|
|
462
496
|
// function signature: app.all({METHOD}, ...{HANDLERS})
|
|
463
497
|
// example: app.all('post', () => {});
|
|
464
498
|
if (typeof path === 'string' && HTTP_VERBS.includes(path.toLowerCase()))
|
|
465
499
|
for (let handler of handlers)
|
|
466
500
|
this.#setRoute(path, MATCH_ANY, handler);
|
|
467
|
-
|
|
468
|
-
|
|
469
501
|
// function signature: app.all({PATH}, ...{HANDLERS})
|
|
470
502
|
// example: app.all('/api', () => {});
|
|
471
503
|
else if (typeof path === 'string')
|
|
472
504
|
for (let handler of handlers)
|
|
473
|
-
for (let verb of HTTP_VERBS)
|
|
505
|
+
for (let verb of HTTP_VERBS)
|
|
474
506
|
this.#setRoute(verb, path, handler);
|
|
475
|
-
|
|
476
507
|
// function signature: app.all(...{HANDLERS})
|
|
477
|
-
// example: app.all(() => {});
|
|
508
|
+
// example: app.all(() => {});
|
|
478
509
|
else if (typeof path === 'function')
|
|
479
510
|
for (let verb of HTTP_VERBS) {
|
|
480
511
|
this.#setRoute(verb, MATCH_ANY, path);
|
|
481
512
|
for (let handler of handlers)
|
|
482
513
|
this.#setRoute(verb, MATCH_ANY, handler);
|
|
483
514
|
}
|
|
515
|
+
else
|
|
516
|
+
throw new Error(
|
|
517
|
+
'<SlowerMicroRouter>.use :: "handler" parameter must be of type Function'
|
|
518
|
+
);
|
|
484
519
|
|
|
485
|
-
|
|
486
|
-
else throw new Error('<SlowerMicroRouter>.use :: "handler" parameter must be of type Function');
|
|
487
|
-
|
|
488
520
|
return this;
|
|
489
521
|
}
|
|
490
522
|
// Just a more comprehensive call to app.all for defining middlewares
|
|
491
|
-
use
|
|
523
|
+
use(...b) {
|
|
524
|
+
this.all(...b);
|
|
525
|
+
return this;
|
|
526
|
+
}
|
|
492
527
|
}
|
|
493
528
|
|
|
494
|
-
|
|
495
529
|
// Create basic main routing creation
|
|
496
|
-
function slower
|
|
530
|
+
function slower(options) {
|
|
497
531
|
return new SlowerRouter(options);
|
|
498
532
|
}
|
|
533
|
+
|
|
499
534
|
// Add the subrouter class
|
|
500
535
|
slower.Router = function () {
|
|
501
536
|
return new SlowerSubRouter();
|
|
502
|
-
}
|
|
537
|
+
};
|
|
503
538
|
|
|
504
539
|
module.exports = slower;
|
package/src/utils.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
const { statSync, readdirSync } = require('node:fs')
|
|
1
|
+
const { statSync, readdirSync } = require('node:fs');
|
|
3
2
|
const { join } = require('node:path');
|
|
4
3
|
const { parse } = require('node:querystring');
|
|
5
4
|
|
|
6
5
|
function* getFiles(folder) {
|
|
7
6
|
const files = readdirSync(folder);
|
|
8
7
|
for (const file of files) {
|
|
9
|
-
const absolutePath = join(folder, file)
|
|
8
|
+
const absolutePath = join(folder, file);
|
|
10
9
|
if (statSync(absolutePath).isDirectory()) {
|
|
11
|
-
yield* getFiles(absolutePath)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
yield absolutePath.replaceAll('..\\','')
|
|
10
|
+
yield* getFiles(absolutePath);
|
|
11
|
+
} else {
|
|
12
|
+
yield absolutePath.replaceAll('..\\', '');
|
|
15
13
|
}
|
|
16
14
|
}
|
|
17
15
|
}
|
|
@@ -22,44 +20,48 @@ const getMatchingRoute = (url, method, layers) => {
|
|
|
22
20
|
// Get only the layers from the proper HTTP verb
|
|
23
21
|
let routes = layers.get(method) || new Map();
|
|
24
22
|
// Iterate through all routes, and get the one that match
|
|
25
|
-
for (let [
|
|
23
|
+
for (let [pathFn, callback] of routes) {
|
|
26
24
|
let params = pathFn(getURLPathBody(url));
|
|
27
25
|
// Return the matching route
|
|
28
|
-
if (
|
|
26
|
+
if (params) {
|
|
29
27
|
// add to list
|
|
30
|
-
let built =
|
|
28
|
+
let built = { callback };
|
|
31
29
|
if (params.params && params.params['0'] === undefined)
|
|
32
30
|
built['params'] = params.params;
|
|
33
31
|
list.push(built);
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
34
|
return list;
|
|
37
|
-
}
|
|
35
|
+
};
|
|
38
36
|
|
|
39
|
-
const normalizeAddress = addr =>
|
|
37
|
+
const normalizeAddress = addr =>
|
|
38
|
+
addr.startsWith('::') ? addr : addr.substring(addr.indexOf(':', 2) + 1);
|
|
40
39
|
|
|
41
|
-
const getFileExtension =
|
|
40
|
+
const getFileExtension = fname => fname.split('.').filter(Boolean).slice(-1)[0];
|
|
42
41
|
|
|
43
42
|
// /page?foo=bar&abc=123 -> /page
|
|
44
43
|
const getURLPathBody = (urlPath = '') => urlPath.split('?')[0] || '';
|
|
45
44
|
|
|
46
45
|
// /page?foo=bar&abc=123 -> { foo: 'bar', abc: '123' }
|
|
47
|
-
const getURLQueryString = (urlPath = '') =>
|
|
48
|
-
|
|
49
|
-
const noLayersFoundFallback = (req, res) =>
|
|
50
|
-
res.status(404).send(
|
|
51
|
-
`<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">` +
|
|
52
|
-
`<title>Error</title></head><body>` +
|
|
53
|
-
`<pre>Cannot ${req.method.toUpperCase()} ${req.url}</pre></body></html>`
|
|
54
|
-
)
|
|
55
|
-
;
|
|
46
|
+
const getURLQueryString = (urlPath = '') =>
|
|
47
|
+
parse((urlPath.split('?')[1] || '').split('#')[0]);
|
|
56
48
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
49
|
+
const noLayersFoundFallback = (req, res) =>
|
|
50
|
+
res
|
|
51
|
+
.status(404)
|
|
52
|
+
.send(
|
|
53
|
+
`<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">` +
|
|
54
|
+
`<title>Error</title></head><body>` +
|
|
55
|
+
`<pre>Cannot ${req.method.toUpperCase()} ${
|
|
56
|
+
req.url
|
|
57
|
+
}</pre></body></html>`
|
|
58
|
+
);
|
|
59
|
+
module.exports = {
|
|
60
|
+
getMatchingRoute,
|
|
61
|
+
getFiles,
|
|
62
|
+
normalizeAddress,
|
|
63
|
+
getFileExtension,
|
|
64
|
+
getURLPathBody,
|
|
63
65
|
getURLQueryString,
|
|
64
|
-
noLayersFoundFallback
|
|
65
|
-
};
|
|
66
|
+
noLayersFoundFallback,
|
|
67
|
+
};
|