slower 1.1.8
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/index.js +1 -0
- package/lib/router.js +316 -0
- package/lib/utils.js +74 -0
- package/package.json +16 -0
- package/readme.md +48 -0
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./lib/router');
|
package/lib/router.js
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
const http = require('http');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const { noop, slugify, isSparseEqual, last, renderDynamicHTML } = require('./utils');
|
|
4
|
+
|
|
5
|
+
class Route {
|
|
6
|
+
constructor (path, type, callback) {
|
|
7
|
+
this.path = (path.startsWith('/') ? path : '/' + path);
|
|
8
|
+
this.type = SlowerRouter.http_methods.includes(slugify(type).toUpperCase()) ? slugify(type).toUpperCase() : null;
|
|
9
|
+
this.callback = (typeof callback == 'function' ? callback : noop);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class SlowerRouter {
|
|
14
|
+
static http_methods = [
|
|
15
|
+
'GET', 'POST', 'PUT',
|
|
16
|
+
'HEAD', 'DELETE', 'OPTIONS',
|
|
17
|
+
'TRACE', 'COPY', 'LOCK',
|
|
18
|
+
'MKCOL', 'MOVE', 'PURGE',
|
|
19
|
+
'PROPFIND', 'PROPPATCH', 'UNLOCK',
|
|
20
|
+
'REPORT', 'MKACTIVITY', 'CHECKOUT',
|
|
21
|
+
'MERGE', 'M-SEARCH', 'NOTIFY',
|
|
22
|
+
'SUBSCRIBE', 'UNSUBSCRIBE', 'PATCH',
|
|
23
|
+
'SEARCH', 'CONNECT'
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
static mime_table = {
|
|
27
|
+
'txt' : ['text/plain', 'utf-8'],
|
|
28
|
+
'html': ['text/html', 'utf-8'],
|
|
29
|
+
'js' : ['text/javascript', 'utf-8'],
|
|
30
|
+
'css' : ['text/css', 'utf-8'],
|
|
31
|
+
'ico' : ['image/png'],
|
|
32
|
+
'default': ['application/octet-stream']
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
constructor () {
|
|
36
|
+
this.routes = [];
|
|
37
|
+
this.middleware = [noop];
|
|
38
|
+
this.fallback = noop;
|
|
39
|
+
this.allowedMethods = [];
|
|
40
|
+
this.blockedMethodCallback = noop;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Defines a route for a determined request method
|
|
45
|
+
* @category Router
|
|
46
|
+
* @param {String} path The route that will be defined
|
|
47
|
+
* @param {String} type The method to respond to
|
|
48
|
+
* @param {Function} callback Callback to use when the chosen route is accessed
|
|
49
|
+
* @returns {Object} An Route object
|
|
50
|
+
* @example <caption> Defining a simple GET route:</caption>
|
|
51
|
+
* setRoute('/', 'GET', (req, res) => {
|
|
52
|
+
* console.log(res.url);
|
|
53
|
+
* res.end('received');
|
|
54
|
+
* });
|
|
55
|
+
* // => <Route> { path:..., type:..., callback:... }
|
|
56
|
+
*/
|
|
57
|
+
setRoute = function (path = '/', type = 'GET', callback) {
|
|
58
|
+
let stat = new Route(path, type, callback);
|
|
59
|
+
this.routes.push(stat);
|
|
60
|
+
return stat;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Defines a middleware for all requests
|
|
65
|
+
* @category Middleware
|
|
66
|
+
* @param {Function} callback Callback to use when the requests are made
|
|
67
|
+
* @returns {Array} The used middlewares list
|
|
68
|
+
*/
|
|
69
|
+
setMiddleware = function (callback) {
|
|
70
|
+
return this.middleware.push((typeof callback == 'function' ? callback : noop));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// SERVES FILES FOR SPECIFIC PATHS. WILDCARS ALLOWED. BE CAREFUL - PATH TRAVERSAL MAY BE POSSIBLE
|
|
74
|
+
// Use app.setStatic('/*', __dirname+'/') for serving all local files.
|
|
75
|
+
// or use app.setStatic('/*', __dirname+'/public/') for serving files in a specific folder (public).
|
|
76
|
+
// This is a one-liner for the setRoute function, when the route responds with a simple page or document
|
|
77
|
+
/**
|
|
78
|
+
* Defines a route for a determined request method
|
|
79
|
+
* @category Router
|
|
80
|
+
* @param {String} path The route that will be defined
|
|
81
|
+
* @param {String} file The file path that will be used to respond to the route
|
|
82
|
+
* @param {String} mime The file's mime type
|
|
83
|
+
* @param {Object} replacementData The replacement data map for the dynamic HTML rendering
|
|
84
|
+
* @returns {Object} An Route object already configured
|
|
85
|
+
* @example <caption> Defining a simple GET route:</caption>
|
|
86
|
+
* setStatic('/login', __dirname+'/public/static/views/login.html', 'text/html', 'utf8');
|
|
87
|
+
*/
|
|
88
|
+
/**
|
|
89
|
+
* Dynamic rendering example:
|
|
90
|
+
* It's a template engine, to render HTML containing template spaces.
|
|
91
|
+
* The charset for replacement is <{content}>
|
|
92
|
+
* @since 1.2.5
|
|
93
|
+
*
|
|
94
|
+
* @param {String} html The HTML code
|
|
95
|
+
* @param {Object} patterns The patterns to replace in the HTML code
|
|
96
|
+
* @return {String} The HTML with the templates replaces
|
|
97
|
+
*
|
|
98
|
+
* @example <caption> Rendering: </caption>
|
|
99
|
+
* var template = 'Hello, my name is <{name}>. I\\'m <{age}> years old.';
|
|
100
|
+
* console.log(TemplateEngine(template, {
|
|
101
|
+
* name: "Krasimir",
|
|
102
|
+
* age: 29
|
|
103
|
+
* }));
|
|
104
|
+
*/
|
|
105
|
+
setDynamic = function (path, file = '', mime = '', replacementData) {
|
|
106
|
+
let encoding = (mime === SlowerRouter.mime_table['default'] ? undefined : 'utf-8')
|
|
107
|
+
let stat = new Route(path, 'GET', (req, res) => {
|
|
108
|
+
let data, targetFile, extension, targetMime, targetEncoding;
|
|
109
|
+
if (fs.existsSync(file) && fs.lstatSync(file).isDirectory()) {
|
|
110
|
+
targetFile = file.replace(/\//gim, '\\');
|
|
111
|
+
targetFile = ((targetFile.endsWith('\\')) ? targetFile : targetFile+'\\') + req.url.replace('/', '');
|
|
112
|
+
} else {
|
|
113
|
+
targetFile = ((file == '' || !file) ? req.url : file);
|
|
114
|
+
}
|
|
115
|
+
extension = last(targetFile.split('.'));
|
|
116
|
+
targetMime = mime || (SlowerRouter.mime_table[extension]?.[0] || SlowerRouter.mime_table.default[0]);
|
|
117
|
+
targetEncoding = encoding || (SlowerRouter.mime_table[extension]?.[1] || SlowerRouter.mime_table.default[1]);
|
|
118
|
+
try {
|
|
119
|
+
data = fs.readFileSync(targetFile, targetEncoding);
|
|
120
|
+
} catch (err) {
|
|
121
|
+
data = '';
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
if (replacementData) data = renderDynamicHTML(data, replacementData);
|
|
125
|
+
} catch (err) {}
|
|
126
|
+
res.writeHead(200, { 'Content-Type': targetMime });
|
|
127
|
+
res.write(data);
|
|
128
|
+
res.end();
|
|
129
|
+
});
|
|
130
|
+
this.routes.push(stat);
|
|
131
|
+
return stat;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// SERVES FILES FOR SPECIFIC PATHS. WILDCARS ALLOWED. BE CAREFUL - PATH TRAVERSAL MAY BE POSSIBLE
|
|
135
|
+
// Use app.setStatic('/*', __dirname+'/') for serving all local files.
|
|
136
|
+
// or use app.setStatic('/*', __dirname+'/public/') for serving files in a specific folder (public).
|
|
137
|
+
// This is a one-liner for the setRoute function, when the route responds with a simple page or document
|
|
138
|
+
/**
|
|
139
|
+
* Defines a route for a determined request method
|
|
140
|
+
* @category Router
|
|
141
|
+
* @param {String} path The route that will be defined
|
|
142
|
+
* @param {String} file The file path that will be used to respond to the route
|
|
143
|
+
* @param {String} mime The file's mime type
|
|
144
|
+
* @param {String} encoding The file's encoding (defaults to UTF-8)
|
|
145
|
+
* @returns {Object} An Route object already configured
|
|
146
|
+
* @example <caption> Defining a simple GET route:</caption>
|
|
147
|
+
* setStatic('/login', __dirname+'/public/static/views/login.html', 'text/html', 'utf8');
|
|
148
|
+
*/
|
|
149
|
+
setStatic = function (path, file = '', mime = '') {
|
|
150
|
+
let encoding = (mime === SlowerRouter.mime_table['default'] ? undefined : 'utf-8')
|
|
151
|
+
let stat = new Route(path, 'GET', (req, res) => {
|
|
152
|
+
let data, targetFile, extension, targetMime, targetEncoding;
|
|
153
|
+
if (fs.existsSync(file) && fs.lstatSync(file).isDirectory()) {
|
|
154
|
+
targetFile = file.replace(/\//gim, '\\');
|
|
155
|
+
targetFile = ((targetFile.endsWith('\\')) ? targetFile : targetFile+'\\') + req.url.replace('/', '');
|
|
156
|
+
} else {
|
|
157
|
+
targetFile = ((file == '' || !file) ? req.url : file);
|
|
158
|
+
}
|
|
159
|
+
extension = last(targetFile.split('.'));
|
|
160
|
+
targetMime = mime || (SlowerRouter.mime_table[extension]?.[0] || SlowerRouter.mime_table.default[0]);
|
|
161
|
+
targetEncoding = encoding || (SlowerRouter.mime_table[extension]?.[1] || SlowerRouter.mime_table.default[1]);
|
|
162
|
+
try {
|
|
163
|
+
data = fs.readFileSync(targetFile, targetEncoding);
|
|
164
|
+
} catch (err) {
|
|
165
|
+
data = '';
|
|
166
|
+
}
|
|
167
|
+
res.writeHead(200, { 'Content-Type': targetMime });
|
|
168
|
+
res.write(data);
|
|
169
|
+
res.end();
|
|
170
|
+
});
|
|
171
|
+
this.routes.push(stat);
|
|
172
|
+
return stat;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Defines a default response for non-defined routes (custom treated 404 error)
|
|
177
|
+
* @category Router
|
|
178
|
+
* @param {String} callback The function to execute for unhandled routes
|
|
179
|
+
* @returns {undefined}
|
|
180
|
+
* @example <caption> Defining a simple GET route:</caption>
|
|
181
|
+
* setStatic('/login', __dirname+'/public/static/views/login.html', 'text/html', 'utf8');
|
|
182
|
+
*/
|
|
183
|
+
setFallback = function (callback) { this.fallback = callback; }
|
|
184
|
+
|
|
185
|
+
// SERVES FILES FOR SPECIFIC PATHS. WILDCARS ALLOWED. BE CAREFUL - PATH TRAVERSAL MAY BE POSSIBLE
|
|
186
|
+
// Use app.setStatic('/*', __dirname+'/') for serving all local files.
|
|
187
|
+
// or use app.setStatic('/*', __dirname+'/public/') for serving files in a specific folder (public).
|
|
188
|
+
// This is a one-liner for the setRoute function, when the route responds with a simple page or document
|
|
189
|
+
/**
|
|
190
|
+
* Defines a route for a determined request method
|
|
191
|
+
* @category Router
|
|
192
|
+
* @param {String} path The route that will be defined
|
|
193
|
+
* @param {String} file The file path that will be used to respond to the route
|
|
194
|
+
* @param {String} mime The file's mime type
|
|
195
|
+
* @param {Object} replacementData The replacement data map for the dynamic HTML rendering
|
|
196
|
+
* @returns {Object} An Route object already configured
|
|
197
|
+
* @example <caption> Defining a simple GET route:</caption>
|
|
198
|
+
* setStatic('/login', __dirname+'/public/static/views/login.html', 'text/html', 'utf8');
|
|
199
|
+
*/
|
|
200
|
+
/**
|
|
201
|
+
* Dynamic rendering example:
|
|
202
|
+
* It's a template engine, to render HTML containing template spaces.
|
|
203
|
+
* The charset for replacement is <{content}>
|
|
204
|
+
* @since 1.2.5
|
|
205
|
+
*
|
|
206
|
+
* @param {String} html The HTML code
|
|
207
|
+
* @param {Object} patterns The patterns to replace in the HTML code
|
|
208
|
+
* @return {String} The HTML with the templates replaces
|
|
209
|
+
*
|
|
210
|
+
* @example <caption> Rendering: </caption>
|
|
211
|
+
* var template = 'Hello, my name is <{name}>. I\\'m <{age}> years old.';
|
|
212
|
+
* console.log(TemplateEngine(template, {
|
|
213
|
+
* name: "Krasimir",
|
|
214
|
+
* age: 29
|
|
215
|
+
* }));
|
|
216
|
+
*/
|
|
217
|
+
setFallbackFile = function (file = '', mime = '', replacementData) {
|
|
218
|
+
this.fallback = function fb (req,res) {
|
|
219
|
+
let encoding = (mime === SlowerRouter.mime_table['default'] ? undefined : 'utf-8')
|
|
220
|
+
let data, targetFile, extension, targetMime, targetEncoding;
|
|
221
|
+
if (fs.existsSync(file) && fs.lstatSync(file).isDirectory()) {
|
|
222
|
+
targetFile = file.replace(/\//gim, '\\');
|
|
223
|
+
targetFile = ((targetFile.endsWith('\\')) ? targetFile : targetFile+'\\') + req.url.replace('/', '');
|
|
224
|
+
} else {
|
|
225
|
+
targetFile = ((file == '' || !file) ? req.url : file);
|
|
226
|
+
}
|
|
227
|
+
extension = last(targetFile.split('.'));
|
|
228
|
+
targetMime = mime || (SlowerRouter.mime_table[extension]?.[0] || SlowerRouter.mime_table.default[0]);
|
|
229
|
+
targetEncoding = encoding || (SlowerRouter.mime_table[extension]?.[1] || SlowerRouter.mime_table.default[1]);
|
|
230
|
+
try {
|
|
231
|
+
data = fs.readFileSync(targetFile, targetEncoding);
|
|
232
|
+
} catch (err) {
|
|
233
|
+
data = '';
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
if (replacementData) data = renderDynamicHTML(data, replacementData);
|
|
237
|
+
} catch (err) {}
|
|
238
|
+
res.writeHead(200, { 'Content-Type': targetMime });
|
|
239
|
+
res.write(data);
|
|
240
|
+
res.end();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Sets the methods that the application will respond to. The rest is simply discarded with empty responses.
|
|
245
|
+
// To configure a deeper level of error handling, or to serve customized 'METHOD_NOT_ALLOWED' errors,
|
|
246
|
+
// use 'setRoute' with params ('/*', {{method_name}}, (req,res) => { and serve the error file here });
|
|
247
|
+
// INFO: Middlewares are still triggered when a blocked method request comes.
|
|
248
|
+
/**
|
|
249
|
+
* Defines a route for a determined request method
|
|
250
|
+
* @category Router
|
|
251
|
+
* @param {Array} methods The methods that are allowed by the application. Methods that do not conform with standards are ignored.
|
|
252
|
+
* @returns {Object} The AllowedMethods Array object.
|
|
253
|
+
* @example <caption> Allowing only GET and POST:</caption>
|
|
254
|
+
* app.setAllowedMethods(['GET', 'POST']);
|
|
255
|
+
*/
|
|
256
|
+
setAllowedMethods = function (methods = []) {
|
|
257
|
+
// If not specified, all methods are allowed
|
|
258
|
+
if (methods.length == 0 || methods == '*') {
|
|
259
|
+
return this.allowedMethods = JSON.parse(JSON.stringify(SlowerRouter.http_methods));
|
|
260
|
+
}
|
|
261
|
+
for (let i = 0; i < methods.length; i++) {
|
|
262
|
+
if (!SlowerRouter.http_methods.includes(methods[i])) continue;
|
|
263
|
+
this.allowedMethods.push(methods[i]);
|
|
264
|
+
}
|
|
265
|
+
// Default callback for blocked methods respond with code 200 and an empty response
|
|
266
|
+
this.blockedMethodCallback = function (req,res) {
|
|
267
|
+
res.writeHead(405); // sends the meme error 418 'i am a teapot'
|
|
268
|
+
res.end();
|
|
269
|
+
};
|
|
270
|
+
return this.allowedMethods;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
start = function (port = 8080, callback) {
|
|
274
|
+
let routes = this.routes;
|
|
275
|
+
let middle = this.middleware;
|
|
276
|
+
let fallback = this.fallback;
|
|
277
|
+
let allowedMethods = this.allowedMethods;
|
|
278
|
+
let blockedMethodCallback = this.blockedMethodCallback;
|
|
279
|
+
|
|
280
|
+
let server = http.createServer(function (req, res) {
|
|
281
|
+
try {
|
|
282
|
+
// Runs all middlewares
|
|
283
|
+
for (let i = 0; i < middle.length; i++) { middle[i](req, res); }
|
|
284
|
+
// Only respond to allowed methods with callbacks, else, use the default empty response.
|
|
285
|
+
if (
|
|
286
|
+
allowedMethods.includes(req.method)) {
|
|
287
|
+
for (let i = 0; i < routes.length; i++) {
|
|
288
|
+
let route = routes[i];
|
|
289
|
+
if (
|
|
290
|
+
route.type === req.method &&
|
|
291
|
+
(
|
|
292
|
+
route.path === req.url ||
|
|
293
|
+
isSparseEqual(route.path, req.url)
|
|
294
|
+
)
|
|
295
|
+
) {
|
|
296
|
+
i = routes.length;
|
|
297
|
+
route.callback(req, res);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
fallback(req,res);
|
|
302
|
+
} else {
|
|
303
|
+
blockedMethodCallback(req, res);
|
|
304
|
+
}
|
|
305
|
+
} catch(err) {
|
|
306
|
+
console.log(err);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
callback(server);
|
|
310
|
+
return server.listen(port);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const Slower = function () { return new SlowerRouter(); }
|
|
315
|
+
|
|
316
|
+
module.exports = Slower;
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const noop = function () {};
|
|
2
|
+
|
|
3
|
+
const last = (array) => { return array[array.length-1]; }
|
|
4
|
+
|
|
5
|
+
const slugify = (string, replacement = '-', replaceSpaces = true) => {
|
|
6
|
+
return (string
|
|
7
|
+
.replace(/<(?:.|\n)*?>/gm, '')
|
|
8
|
+
.replace(/[!\"#$%&'\(\)\*\+,\/:;<=>\?\@\[\\\]\^`\{\|\}~]/g, '')
|
|
9
|
+
.replace((replaceSpaces ? /(\s|\.)/g : /(\.)/g), replacement)
|
|
10
|
+
.replace(/—/g, replacement)
|
|
11
|
+
.replace(/-{2,}/g, replacement));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Compares two strings in 'sparse' mode. Using the wildcards '{*}' and '{?}' to match strings.
|
|
16
|
+
* Use '{*}' for any number of (any) characters, and {?}' for one (any) character.
|
|
17
|
+
*
|
|
18
|
+
* @since 1.2.7
|
|
19
|
+
*
|
|
20
|
+
* @param {String} str1 The first string to compare
|
|
21
|
+
* @param {String} str2 The second string to compare
|
|
22
|
+
* @return {Boolean} If the strings are sparsely equal or not
|
|
23
|
+
* @example <caption> Comparing simple strings: </caption>
|
|
24
|
+
* isSparseEqual("hello", "hello")
|
|
25
|
+
* // => true
|
|
26
|
+
* isSparseEqual("hello", "wello")
|
|
27
|
+
* // => false
|
|
28
|
+
* @example <caption> Comparing complex strings: </caption>
|
|
29
|
+
* isSparseEqual("{?}ello", "hello")
|
|
30
|
+
* // => true
|
|
31
|
+
* isSparseEqual("h*", "hello")
|
|
32
|
+
* // => true
|
|
33
|
+
* isSparseEqual("h{*}e", "hello")
|
|
34
|
+
* // => false
|
|
35
|
+
* isSparseEqual("h{*}e", "helle")
|
|
36
|
+
* // => true
|
|
37
|
+
*/
|
|
38
|
+
const isSparseEqual = (str1 = '', str2 = '') => {
|
|
39
|
+
const string1 = str1.replace(/{\?}/g, '.').replace(/{\*}/g, '.*');
|
|
40
|
+
const string2 = str2.replace(/{\?}/g, '.').replace(/{\*}/g, '.*');
|
|
41
|
+
const regex = new RegExp(`^${string1}$`);
|
|
42
|
+
return regex.test(string2);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* It's a template engine, to render HTML containing template spaces.
|
|
47
|
+
* The charset for replacement is <{content}>
|
|
48
|
+
* @since 1.2.5
|
|
49
|
+
*
|
|
50
|
+
* @param {String} html The HTML code
|
|
51
|
+
* @param {Object} patterns The patterns to replace in the HTML code
|
|
52
|
+
* @return {String} The HTML with the templates replaces
|
|
53
|
+
*
|
|
54
|
+
* @example <caption> Rendering: </caption>
|
|
55
|
+
* var template = 'Hello, my name is <{name}>. I\\'m <{age}> years old.';
|
|
56
|
+
* console.log(TemplateEngine(template, {
|
|
57
|
+
* name: "Krasimir",
|
|
58
|
+
* age: 29
|
|
59
|
+
* }));
|
|
60
|
+
*/
|
|
61
|
+
const renderDynamicHTML = (html, patterns) => {
|
|
62
|
+
let template = html;
|
|
63
|
+
for (let item in patterns) {
|
|
64
|
+
template = html.replace(
|
|
65
|
+
new RegExp('<{'+item+'}>', 'gim'),
|
|
66
|
+
patterns[item]
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return template;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const toBool = [() => true, () => false];
|
|
73
|
+
|
|
74
|
+
module.exports = { noop, slugify, isSparseEqual, toBool, last, renderDynamicHTML };
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "slower",
|
|
3
|
+
"version": "1.1.8",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"directories": {
|
|
7
|
+
"doc": "doc",
|
|
8
|
+
"lib": "lib"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [],
|
|
14
|
+
"author": "",
|
|
15
|
+
"license": "ISC"
|
|
16
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Slower
|
|
2
|
+
|
|
3
|
+
Slower is a small web framework, express-like, but simpler and limited.
|
|
4
|
+
It allows for generic route-declaration, fallback pages, and multiple middleware functions.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example usage:
|
|
8
|
+
```
|
|
9
|
+
const Slower = require('.');
|
|
10
|
+
const port = 8080;
|
|
11
|
+
|
|
12
|
+
let app = Slower();
|
|
13
|
+
|
|
14
|
+
app.setMiddleware((req, res) => {
|
|
15
|
+
req.time = Date.now();
|
|
16
|
+
console.log(`${req.time} - ${req.method} : ${req.url}`);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
app.setStatic('/favicon.ico', __dirname+'/public/favicon.ico');
|
|
20
|
+
|
|
21
|
+
app.setRoute('/', 'GET', (req, res) => {
|
|
22
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
23
|
+
res.write('<html><body><p>This is the / page.</p></body></html>');
|
|
24
|
+
res.end();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
app.setRoute('/{*}.css', 'GET', (req, res) => {
|
|
28
|
+
let data = fs.readFileSync(...some.css.file...)
|
|
29
|
+
res.writeHead(200, { 'Content-Type': 'text/css' });
|
|
30
|
+
res.write(data);
|
|
31
|
+
res.end();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const generateDownloadNumber = () => { Math.round(Math.random() * 10) }
|
|
35
|
+
app.setDynamic('/download/{?}/', './main-download.txt', { DowloadName: generateDownloadNumber });
|
|
36
|
+
// Responds to download routes such as '/download/2/'
|
|
37
|
+
|
|
38
|
+
app.setFallback((req, res) => {
|
|
39
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
40
|
+
res.write('<html><body><p>This is the fallback page.</p></body></html>');
|
|
41
|
+
res.end();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
app.start(port, () => {
|
|
45
|
+
console.log(`Running on localhost:${port}`);
|
|
46
|
+
console.log(app);
|
|
47
|
+
});
|
|
48
|
+
```
|