slower 2.1.8 → 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 +3 -7
- 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
|
@@ -11,7 +11,7 @@ const utils = require('./utils');
|
|
|
11
11
|
* @exposes .json()
|
|
12
12
|
* @exposes .file()
|
|
13
13
|
*/
|
|
14
|
-
function setupResponse(response) {
|
|
14
|
+
function setupResponse(response, request) {
|
|
15
15
|
/**
|
|
16
16
|
* Sets the response status code
|
|
17
17
|
* @chainable
|
|
@@ -67,11 +67,7 @@ function setupResponse(response) {
|
|
|
67
67
|
* @info And if no type is specified, binary type is used (application/octet-stream)
|
|
68
68
|
*/
|
|
69
69
|
response.type = function (mime) {
|
|
70
|
-
let mimetype =
|
|
71
|
-
MIME_TABLE[mime] ||
|
|
72
|
-
mime ||
|
|
73
|
-
MIME_TABLE[extension] ||
|
|
74
|
-
MIME_TABLE['default'];
|
|
70
|
+
let mimetype = MIME_TABLE[mime] || mime || MIME_TABLE['default'];
|
|
75
71
|
response.setHeader('Content-type', mimetype);
|
|
76
72
|
return response;
|
|
77
73
|
};
|
|
@@ -193,7 +189,7 @@ function setupRequest(request) {
|
|
|
193
189
|
* @property
|
|
194
190
|
* Holds the request body data as a buffer
|
|
195
191
|
*/
|
|
196
|
-
return new Promise(resolve => {
|
|
192
|
+
return new Promise((resolve, reject) => {
|
|
197
193
|
// Set classical socket locals
|
|
198
194
|
request.session = {
|
|
199
195
|
port: request.socket.localPort,
|
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
|
+
};
|