nodester 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/Readme.md +16 -55
  2. package/lib/application/index.js +174 -63
  3. package/lib/body/extract.js +15 -4
  4. package/lib/constants/Bounds.js +15 -0
  5. package/lib/constants/Clauses.js +13 -0
  6. package/lib/constants/ResponseFormats.js +2 -2
  7. package/lib/controllers/methods/index.js +7 -0
  8. package/lib/controllers/mixins/index.js +36 -36
  9. package/lib/database/connection.js +6 -0
  10. package/lib/database/migration.js +14 -4
  11. package/lib/facades/methods/index.js +10 -9
  12. package/lib/facades/mixins/index.js +67 -13
  13. package/lib/factories/responses/rest.js +25 -13
  14. package/lib/http/{request.js → request/index.js} +53 -75
  15. package/lib/http/request/utils.js +27 -0
  16. package/lib/http/response/headers.js +138 -0
  17. package/lib/http/response/index.js +248 -0
  18. package/lib/http/response/utils.js +38 -0
  19. package/lib/middlewares/SearchParams/index.js +25 -0
  20. package/lib/middlewares/cookies/index.js +44 -0
  21. package/lib/middlewares/etag/index.js +32 -15
  22. package/lib/middlewares/formidable/index.js +30 -25
  23. package/lib/middlewares/ql/sequelize/index.js +11 -2
  24. package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +2 -1
  25. package/lib/middlewares/render/index.js +62 -0
  26. package/lib/models/associate.js +25 -1
  27. package/lib/models/define.js +19 -15
  28. package/lib/models/mixins.js +7 -0
  29. package/lib/query/traverse.js +96 -63
  30. package/lib/router/handlers.util.js +1 -0
  31. package/lib/router/index.js +193 -98
  32. package/lib/router/markers.js +7 -0
  33. package/lib/router/route.js +5 -0
  34. package/lib/router/routes.util.js +12 -14
  35. package/lib/router/utils.js +7 -0
  36. package/lib/stacks/MarkersStack.js +41 -3
  37. package/lib/stacks/MiddlewaresStack.js +200 -0
  38. package/lib/structures/Enum.js +46 -0
  39. package/lib/structures/Filter.js +156 -0
  40. package/lib/structures/Params.js +55 -0
  41. package/lib/tools/sql.tool.js +7 -0
  42. package/lib/utils/objects.util.js +31 -24
  43. package/lib/utils/sanitizations.util.js +10 -4
  44. package/lib/validators/arguments.js +68 -0
  45. package/lib/validators/dates.js +7 -0
  46. package/lib/validators/numbers.js +7 -0
  47. package/package.json +11 -8
  48. package/lib/database/utils.js +0 -19
  49. package/lib/enums/Enum.js +0 -16
  50. package/lib/filters/Filter.js +0 -109
  51. package/lib/http/response.js +0 -1074
  52. package/lib/http/utils.js +0 -254
  53. package/lib/params/Params.js +0 -37
  54. package/lib/policies/Role.js +0 -77
  55. package/lib/policies/RoleExtracting.js +0 -97
  56. package/lib/services/includes.service.js +0 -79
  57. package/lib/services/jwt.service.js +0 -147
  58. package/lib/stacks/MiddlewareStack.js +0 -159
@@ -2,17 +2,21 @@
2
2
  * /nodester
3
3
  * MIT Licensed
4
4
  */
5
+
5
6
  'use strict';
6
7
 
8
+ const { IncomingMessage } = require('http');
9
+
7
10
  const accepts = require('accepts');
8
11
  const isIP = require('net').isIP;
9
12
  const typeis = require('type-is');
10
- const { IncomingMessage } = require('http');
11
13
  const fresh = require('fresh');
12
14
  const parseRange = require('range-parser');
13
15
  const parse = require('parseurl');
14
16
  const proxyaddr = require('proxy-addr');
15
17
 
18
+ const { defineGetter } = require('./utils');
19
+
16
20
 
17
21
  const req = IncomingMessage.prototype;
18
22
 
@@ -38,8 +42,10 @@ module.exports = req;
38
42
  * Aliased as `req.header()`.
39
43
  *
40
44
  * @param {String} name
45
+ *
41
46
  * @return {String}
42
- * @public
47
+ *
48
+ * @api public
43
49
  */
44
50
  req.get =
45
51
  req.header = function header(name) {
@@ -106,7 +112,8 @@ req.header = function header(name) {
106
112
  *
107
113
  * @param {String|Array} type(s)
108
114
  * @return {String|Array|Boolean}
109
- * @public
115
+ *
116
+ * @api public
110
117
  */
111
118
  req.accepts = function() {
112
119
  const accept = accepts(this);
@@ -119,7 +126,8 @@ req.accepts = function() {
119
126
  *
120
127
  * @param {String} ...encoding
121
128
  * @return {String|Array}
122
- * @public
129
+ *
130
+ * @api public
123
131
  */
124
132
  req.acceptsEncodings = function() {
125
133
  const accept = accepts(this);
@@ -133,7 +141,8 @@ req.acceptsEncodings = function() {
133
141
  *
134
142
  * @param {String} ...charset
135
143
  * @return {String|Array}
136
- * @public
144
+ *
145
+ * @api public
137
146
  */
138
147
  req.acceptsCharsets = function() {
139
148
  const accept = accepts(this);
@@ -147,7 +156,8 @@ req.acceptsCharsets = function() {
147
156
  *
148
157
  * @param {String} ...lang
149
158
  * @return {String|Array}
150
- * @public
159
+ *
160
+ * @api public
151
161
  */
152
162
  req.acceptsLanguages = function() {
153
163
  const accept = accepts(this);
@@ -176,8 +186,10 @@ req.acceptsLanguages = function() {
176
186
  * @param {number} size
177
187
  * @param {object} [options]
178
188
  * @param {boolean} [options.combine=false]
189
+ *
179
190
  * @return {number|array}
180
- * @public
191
+ *
192
+ * @api public
181
193
  */
182
194
  req.range = function range(size, options) {
183
195
  const range = this.get('Range');
@@ -211,7 +223,8 @@ req.range = function range(size, options) {
211
223
  *
212
224
  * @param {String|Array} types...
213
225
  * @return {String|false|null}
214
- * @public
226
+ *
227
+ * @api public
215
228
  */
216
229
  req.is = function is(types) {
217
230
  let arr = types;
@@ -239,7 +252,8 @@ req.is = function is(types) {
239
252
  * supplies https for you this may be enabled.
240
253
  *
241
254
  * @return {String}
242
- * @public
255
+ *
256
+ * @api public
243
257
  */
244
258
 
245
259
  defineGetter(req, 'protocol', function protocol() {
@@ -254,12 +268,13 @@ defineGetter(req, 'protocol', function protocol() {
254
268
 
255
269
  // Note: X-Forwarded-Proto is normally only ever a
256
270
  // single value, but this is to be safe.
257
- const header = this.get('X-Forwarded-Proto') || proto
258
- const index = header.indexOf(',')
271
+ const header = this.get('X-Forwarded-Proto') || proto;
272
+ const index = header.indexOf(',');
259
273
 
260
- return index !== -1
261
- ? header.substring(0, index).trim()
262
- : header.trim()
274
+ return index !== -1 ?
275
+ header.substring(0, index).trim()
276
+ :
277
+ header.trim();
263
278
  });
264
279
 
265
280
 
@@ -269,7 +284,8 @@ defineGetter(req, 'protocol', function protocol() {
269
284
  * req.protocol === 'https'
270
285
  *
271
286
  * @return {Boolean}
272
- * @public
287
+ *
288
+ * @api public
273
289
  */
274
290
  defineGetter(req, 'secure', function secure() {
275
291
  return this.protocol === 'https';
@@ -283,7 +299,8 @@ defineGetter(req, 'secure', function secure() {
283
299
  * "trust proxy" is set.
284
300
  *
285
301
  * @return {String}
286
- * @public
302
+ *
303
+ * @api public
287
304
  */
288
305
  defineGetter(req, 'ip', function ip() {
289
306
  const trust = this.app.get('trust proxy fn');
@@ -299,8 +316,9 @@ defineGetter(req, 'ip', function ip() {
299
316
  * where "proxy2" is the furthest down-stream and "proxy1" and
300
317
  * "proxy2" were trusted.
301
318
  *
302
- * @return {Array}
303
- * @public
319
+ * @return {Array} addresses
320
+ *
321
+ * @api public
304
322
  */
305
323
  defineGetter(req, 'ips', function ips() {
306
324
  const trust = this.app.get('trust proxy fn');
@@ -308,37 +326,9 @@ defineGetter(req, 'ips', function ips() {
308
326
 
309
327
  // reverse the order (to farthest -> closest)
310
328
  // and remove socket address
311
- addrs.reverse().pop()
329
+ addresses.reverse().pop()
312
330
 
313
- return addrs
314
- });
315
-
316
-
317
- /**
318
- * Return subdomains as an array.
319
- *
320
- * Subdomains are the dot-separated parts of the host before the main domain of
321
- * the app. By default, the domain of the app is assumed to be the last two
322
- * parts of the host. This can be changed by setting "subdomain offset".
323
- *
324
- * For example, if the domain is "tobi.ferrets.example.com":
325
- * If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.
326
- * If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
327
- *
328
- * @return {Array}
329
- * @public
330
- */
331
- defineGetter(req, 'subdomains', function subdomains() {
332
- const hostname = this.hostname;
333
-
334
- if (!hostname) return [];
335
-
336
- const offset = this.app.get('subdomain offset');
337
- const subdomains = !isIP(hostname)
338
- ? hostname.split('.').reverse()
339
- : [hostname];
340
-
341
- return subdomains.slice(offset);
331
+ return addresses;
342
332
  });
343
333
 
344
334
 
@@ -346,7 +336,8 @@ defineGetter(req, 'subdomains', function subdomains() {
346
336
  * Short-hand for `url.parse(req.url).pathname`.
347
337
  *
348
338
  * @return {String}
349
- * @public
339
+ *
340
+ * @api public
350
341
  */
351
342
  defineGetter(req, 'path', function path() {
352
343
  return parse(this).pathname;
@@ -359,7 +350,8 @@ defineGetter(req, 'path', function path() {
359
350
  * or "Host" as a fallback.
360
351
  *
361
352
  * @return {String}
362
- * @public
353
+ *
354
+ * @api public
363
355
  */
364
356
  defineGetter(req, 'hostname', function hostname() {
365
357
  const host = this.get('X-Forwarded-Host') ?? this.get('Host');
@@ -373,23 +365,24 @@ defineGetter(req, 'hostname', function hostname() {
373
365
  * still match.
374
366
  *
375
367
  * @return {Boolean}
376
- * @public
368
+ *
369
+ * @api public
377
370
  */
378
371
  defineGetter(req, 'fresh', function() {
379
372
  const method = this.method;
380
- const res = this.res
381
- const status = res.statusCode
373
+ const res = this.res;
374
+ const status = res.statusCode;
382
375
 
383
376
  // GET or HEAD for weak freshness validation only
384
- if ('GET' !== method && 'HEAD' !== method)
377
+ if (method !== 'GET' && method !== 'HEAD')
385
378
  return false;
386
379
 
387
380
  // 2xx or 304 as per rfc2616 14.26
388
381
  if ((status >= 200 && status < 300) || 304 === status) {
389
382
  return fresh(this.headers, {
390
- 'etag': res.get('ETag'),
383
+ 'etag': res.get('etag'),
391
384
  'last-modified': res.get('Last-Modified')
392
- })
385
+ });
393
386
  }
394
387
 
395
388
  return false;
@@ -402,7 +395,8 @@ defineGetter(req, 'fresh', function() {
402
395
  * resource has changed.
403
396
  *
404
397
  * @return {Boolean}
405
- * @public
398
+ *
399
+ * @api public
406
400
  */
407
401
  defineGetter(req, 'stale', function stale() {
408
402
  return !this.fresh;
@@ -413,26 +407,10 @@ defineGetter(req, 'stale', function stale() {
413
407
  * Check if the request was an _XMLHttpRequest_.
414
408
  *
415
409
  * @return {Boolean}
416
- * @public
410
+ *
411
+ * @api public
417
412
  */
418
413
  defineGetter(req, 'xhr', function xhr() {
419
414
  const val = this.get('X-Requested-With') || '';
420
415
  return val.toLowerCase() === 'xmlhttprequest';
421
416
  });
422
-
423
-
424
- /**
425
- * Helper function for creating a getter on an object.
426
- *
427
- * @param {Object} obj
428
- * @param {String} name
429
- * @param {Function} getter
430
- * @private
431
- */
432
- function defineGetter(obj, name, getter) {
433
- Object.defineProperty(obj, name, {
434
- configurable: true,
435
- enumerable: true,
436
- get: getter
437
- });
438
- }
@@ -0,0 +1,27 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ module.exports = {
9
+ defineGetter: _defineGetter
10
+ }
11
+
12
+ /**
13
+ * Helper function for creating a getter on an object.
14
+ *
15
+ * @param {Object} obj
16
+ * @param {String} name
17
+ * @param {Function} getter
18
+ *
19
+ * @api private
20
+ */
21
+ function _defineGetter(obj, name, getter) {
22
+ Object.defineProperty(obj, name, {
23
+ configurable: true,
24
+ enumerable: true,
25
+ get: getter
26
+ });
27
+ }
@@ -0,0 +1,138 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ // Constants.
9
+ const CHARSET_REGEX = /;\s*charset\s*=/;
10
+
11
+ // Utils:
12
+ const mime = require('mime');
13
+
14
+
15
+ module.exports = {
16
+ setHeader: _setHeader,
17
+ appendHeader: _appendHeader,
18
+ getHeader: _getHeader,
19
+ removeHeader: _removeHeader,
20
+ }
21
+
22
+ /**
23
+ * Set header `field` to `value`, or pass
24
+ * an object of header fields.
25
+ *
26
+ * Examples:
27
+ *
28
+ * res.set('Foo', ['bar', 'baz']);
29
+ * res.set('Accept', 'application/json');
30
+ * res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
31
+ *
32
+ * Aliased as `res.header()`.
33
+ *
34
+ * @param {String|Object} keyOrObject
35
+ * @param {String|Array} value
36
+ *
37
+ * @return {ServerResponse} for chaining
38
+ *
39
+ * @alias setHeader
40
+ * @api public
41
+ */
42
+ function _setHeader(keyOrObject, value) {
43
+ // As a pair:
44
+ if (arguments.length === 2) {
45
+ let _value = Array.isArray(value) ? value.map(String) : String(value);
46
+
47
+ // Add charset to content-type:
48
+ if (keyOrObject.toLowerCase() === 'content-type') {
49
+
50
+ if (Array.isArray(_value)) {
51
+ throw new TypeError('Content-Type cannot be set to an Array');
52
+ }
53
+
54
+ if (!CHARSET_REGEX.test(_value)) {
55
+ const charset = mime.charsets.getType(_value.split(';')[0]);
56
+
57
+ if (charset) {
58
+ _value += '; charset=' + charset.toLowerCase();
59
+ }
60
+ }
61
+ }
62
+
63
+ this.setHeader(keyOrObject, _value);
64
+ }
65
+ // As an object
66
+ else if (typeof keyOrObject === 'object') {
67
+ for (let key in keyOrObject) {
68
+ this.setHeader(key, keyOrObject[key]);
69
+ }
70
+ }
71
+ else {
72
+ const err = new TypeError(`'keyOrObject' must be of type String|Object.`);
73
+ Error.captureStackTrace(err, _setHeader);
74
+ throw err;
75
+ }
76
+
77
+ return this;
78
+ };
79
+
80
+
81
+ /**
82
+ * Append additional header `field` with value `val`.
83
+ *
84
+ * Example:
85
+ *
86
+ * res.append.header('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
87
+ * res.append.header('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
88
+ * res.append.header('Warning', '199 Miscellaneous warning');
89
+ *
90
+ * @param {String} field
91
+ * @param {String|Array} value
92
+ * @return {ServerResponse} for chaining
93
+ *
94
+ * @alias appendHeader
95
+ * @api public
96
+ */
97
+ function _appendHeader(field, value) {
98
+ const prev = this.get(field);
99
+ let _value = value;
100
+
101
+ if (prev) {
102
+ // concat the new and prev values:
103
+ _value = Array.isArray(prev) ? prev.concat(value)
104
+ : Array.isArray(value) ? [prev].concat(value)
105
+ : [prev, value]
106
+ }
107
+
108
+ return this.setHeader(field, _value);
109
+ };
110
+
111
+
112
+ /**
113
+ * Get value for header `key`.
114
+ *
115
+ * @param {String} key
116
+ *
117
+ * @return {String}
118
+ *
119
+ * @alias getHeader
120
+ * @api public
121
+ */
122
+ function _getHeader(key) {
123
+ return this.getHeader(key);
124
+ };
125
+
126
+ /**
127
+ * Remove value for header `key`.
128
+ *
129
+ * @param {String} key
130
+ *
131
+ * @return {String}
132
+ *
133
+ * @alias removeHeader
134
+ * @api public
135
+ */
136
+ function _removeHeader(key) {
137
+ return this.removeHeader(key);
138
+ };
@@ -0,0 +1,248 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const { ServerResponse } = require('http');
9
+
10
+ const {
11
+ setHeader,
12
+ appendHeader,
13
+ getHeader,
14
+ removeHeader,
15
+ } = require('./headers');
16
+
17
+ const calculateEtag = require('etag');
18
+
19
+ // Utils:
20
+ const statuses = require('statuses');
21
+ const mime = require('mime');
22
+ const {
23
+ setCharset
24
+ } = require('./utils');
25
+
26
+
27
+ const response = ServerResponse.prototype;
28
+
29
+ // Mixin custom methods:
30
+ response.status = _status;
31
+ response.sendStatus = _sendStatus;
32
+
33
+ response.send = _send;
34
+ response.json = _json;
35
+
36
+ // Headers:
37
+ response.set = setHeader;
38
+ response.append = appendHeader;
39
+ response.get = getHeader;
40
+ response.remove = removeHeader;
41
+
42
+ // ContentType:
43
+ response.setContentType = _setContentType;
44
+ response.getContentType = _getContentType;
45
+ // Mixins\
46
+
47
+ module.exports = response;
48
+
49
+ /**
50
+ * Set status `code`.
51
+ *
52
+ * @param {Number} code
53
+ *
54
+ * @return {ServerResponse}
55
+ *
56
+ * @alias status
57
+ * @api public
58
+ */
59
+ function _status(code) {
60
+ this.statusCode = code;
61
+ return this;
62
+ };
63
+
64
+
65
+ /**
66
+ * Send given HTTP status code.
67
+ *
68
+ * Sets the response status to `statusCode` and the body of the
69
+ * response to the standard description from node's http.STATUS_CODES
70
+ * or the statusCode number if no description.
71
+ *
72
+ * Examples:
73
+ *
74
+ * res.sendStatus(200);
75
+ *
76
+ * @param {number} statusCode
77
+ *
78
+ * @alias _sendStatus
79
+ * @api public
80
+ */
81
+ function _sendStatus(statusCode) {
82
+ const body = statuses.message[statusCode] || String(statusCode);
83
+
84
+ this.statusCode = statusCode;
85
+ this.setContentType('txt');
86
+
87
+ return this.send(body);
88
+ };
89
+
90
+ /**
91
+ * Sends a response.
92
+ *
93
+ * Examples:
94
+ *
95
+ * res.send(Buffer.from('wahoo'));
96
+ * res.send({ some: 'json' });
97
+ * res.send('<p>some html</p>');
98
+ *
99
+ * @param {string|number|boolean|object|Buffer} body
100
+ *
101
+ * @alias send
102
+ * @api public
103
+ */
104
+ function _send(body) {
105
+ const req = this.req;
106
+ const app = this.app;
107
+
108
+ let chunk = body;
109
+ let encoding;
110
+ let type;
111
+
112
+ switch (typeof chunk) {
113
+ // strings defaulting to html:
114
+ case 'string':
115
+ if (!this.getContentType()) {
116
+ this.setContentType('html');
117
+ }
118
+ break;
119
+ case 'boolean':
120
+ case 'number':
121
+ case 'object':
122
+ if (chunk === null) {
123
+ chunk = '';
124
+ }
125
+ else if (Buffer.isBuffer(chunk)) {
126
+ if (!this.getContentType()) {
127
+ this.setContentType('bin');
128
+ }
129
+ }
130
+ else {
131
+ return this.json(chunk);
132
+ }
133
+ break;
134
+ }
135
+
136
+ // Write strings in utf-8:
137
+ if (typeof chunk === 'string') {
138
+ encoding = 'utf8';
139
+ type = this.getContentType();
140
+
141
+ // reflect this in content-type
142
+ if (typeof type === 'string') {
143
+ this.setContentType( setCharset(type, 'utf-8') );
144
+ }
145
+ }
146
+
147
+ // Freshness of the REQUEST:
148
+ if (req.fresh) {
149
+ this.statusCode = 304;
150
+ }
151
+
152
+ // Strip irrelevant headers for:
153
+ // - 204 (No Content)
154
+ // - 303 (Not Modified)
155
+ if (
156
+ this.statusCode === 204
157
+ ||
158
+ this.statusCode === 304
159
+ ) {
160
+ this.remove('Content-Type');
161
+ this.remove('Content-Length');
162
+ this.remove('Transfer-Encoding');
163
+ chunk = '';
164
+ }
165
+
166
+ // Alter headers for 205 (Reset Content):
167
+ if (this.statusCode === 205) {
168
+ this.set('Content-Length', '0');
169
+ this.remove('Transfer-Encoding');
170
+ chunk = ''
171
+ }
172
+
173
+ if (req.method === 'HEAD') {
174
+ // skip body for HEAD.
175
+ this.end();
176
+ }
177
+ else {
178
+ // Calculate etag:
179
+ const etag = calculateEtag(chunk);
180
+ this.set('etag', etag);
181
+
182
+ // Respond.
183
+ this.end(chunk, encoding);
184
+ }
185
+
186
+ return this;
187
+ };
188
+
189
+
190
+ /**
191
+ * Send JSON response.
192
+ *
193
+ * @param {string|number|boolean|object} obj
194
+ *
195
+ * @alias json
196
+ * @api public
197
+ */
198
+ function _json(obj) {
199
+
200
+ // Ensure content-type:
201
+ if (!this.getContentType()) {
202
+ this.setContentType('application/json');
203
+ }
204
+
205
+ const body = JSON.stringify(obj);
206
+ return this.send(body);
207
+ };
208
+
209
+ /**
210
+ * Set _Content-Type_ response header with `type` through `mime.getType()`
211
+ * when it does not contain "/", or set the Content-Type to `type` otherwise.
212
+ *
213
+ * Examples:
214
+ *
215
+ * res.type('.html');
216
+ * res.type('html');
217
+ * res.type('json');
218
+ * res.type('application/json');
219
+ * res.type('png');
220
+ *
221
+ * @param {String} type
222
+ *
223
+ * @return {ServerResponse} for chaining
224
+ *
225
+ * @alias setContentType
226
+ * @api public
227
+ */
228
+ function _setContentType(type) {
229
+ const contentType = type.indexOf('/') === -1 ?
230
+ mime.getType(type)
231
+ :
232
+ type;
233
+
234
+ this.setHeader('Content-Type', contentType);
235
+ return this;
236
+ };
237
+
238
+ /**
239
+ * Returns _Content-Type_ header.
240
+ *
241
+ * @return {String} type
242
+ *
243
+ * @alias getContentType
244
+ * @api public
245
+ */
246
+ function _getContentType() {
247
+ return this.getHeader('Content-Type');
248
+ };