nodester 0.1.4 → 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 (63) hide show
  1. package/Readme.md +16 -55
  2. package/lib/application/index.js +174 -63
  3. package/lib/body/extract.js +89 -0
  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 +16 -16
  12. package/lib/facades/mixins/index.js +67 -13
  13. package/lib/factories/responses/rest.js +25 -13
  14. package/lib/http/codes/descriptions.js +82 -0
  15. package/lib/http/codes/index.js +70 -145
  16. package/lib/http/codes/symbols.js +82 -0
  17. package/lib/http/{request.js → request/index.js} +53 -75
  18. package/lib/http/request/utils.js +27 -0
  19. package/lib/http/response/headers.js +138 -0
  20. package/lib/http/response/index.js +248 -0
  21. package/lib/http/response/utils.js +38 -0
  22. package/lib/middlewares/SearchParams/index.js +25 -0
  23. package/lib/middlewares/cookies/index.js +44 -0
  24. package/lib/middlewares/etag/index.js +32 -15
  25. package/lib/middlewares/formidable/index.js +30 -25
  26. package/lib/middlewares/ql/sequelize/index.js +13 -4
  27. package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +4 -3
  28. package/lib/middlewares/render/index.js +62 -0
  29. package/lib/models/associate.js +25 -1
  30. package/lib/models/define.js +26 -19
  31. package/lib/models/mixins.js +8 -1
  32. package/lib/{queries → query}/traverse.js +118 -77
  33. package/lib/router/handlers.util.js +1 -0
  34. package/lib/router/index.js +194 -99
  35. package/lib/router/markers.js +7 -0
  36. package/lib/router/route.js +5 -0
  37. package/lib/router/routes.util.js +16 -14
  38. package/lib/router/utils.js +7 -0
  39. package/lib/stacks/MarkersStack.js +41 -3
  40. package/lib/stacks/MiddlewaresStack.js +200 -0
  41. package/lib/structures/Enum.js +46 -0
  42. package/lib/structures/Filter.js +156 -0
  43. package/lib/structures/Params.js +55 -0
  44. package/lib/tools/sql.tool.js +7 -0
  45. package/lib/utils/objects.util.js +31 -24
  46. package/lib/utils/sanitizations.util.js +10 -4
  47. package/lib/validators/arguments.js +68 -0
  48. package/lib/validators/dates.js +7 -0
  49. package/lib/validators/numbers.js +7 -0
  50. package/package.json +20 -10
  51. package/lib/database/utils.js +0 -19
  52. package/lib/enums/Enum.js +0 -16
  53. package/lib/http/response.js +0 -1074
  54. package/lib/http/utils.js +0 -254
  55. package/lib/params/Params.js +0 -37
  56. package/lib/policies/Role.js +0 -77
  57. package/lib/policies/RoleExtracting.js +0 -97
  58. package/lib/preprocessors/BodyPreprocessor.js +0 -61
  59. package/lib/queries/Colander.js +0 -107
  60. package/lib/queries/NodesterQueryParams.js +0 -145
  61. package/lib/services/includes.service.js +0 -79
  62. package/lib/services/jwt.service.js +0 -147
  63. package/lib/stacks/MiddlewareStack.js +0 -159
@@ -1,145 +0,0 @@
1
- /*!
2
- * /nodester
3
- * MIT Licensed
4
- */
5
- 'use strict';
6
-
7
- // Dictionary of unsafe characters:
8
- const NOT_ALLOWED = [
9
- '{',
10
- '}',
11
- // `\`,
12
- '^',
13
- '~',
14
- '[',
15
- ']',
16
- '`'
17
- ];
18
-
19
- const util = require('util');
20
-
21
- /*
22
- * NodesterQueryParams is a ready-to-use replacement for URLSearchParams.
23
- * The only difference is that NodesterQueryParams
24
- * respects nested "&" during parsing.
25
- */
26
- module.exports = class NodesterQueryParams {
27
- constructor(queryString='') {
28
- // Type validateion:
29
- if (typeof queryString !== 'string') {
30
- const err = new TypeError(`'query' must be a String.`);
31
- throw err;
32
- }
33
-
34
- // You never know if it's encoded or not.
35
- const decoded = decodeURI(queryString);
36
-
37
- // Indicates, how deep the char is inside different ().
38
- let deep = 0;
39
-
40
- const paramLevels = {};
41
-
42
- // Current query parameter.
43
- let param = '';
44
-
45
- // Current query token.
46
- let token = '';
47
-
48
- this._map = new Map();
49
-
50
- for (let i=0; i < decoded.length; i++) {
51
- const char = decoded[i];
52
-
53
- // Validate char:
54
- if (NOT_ALLOWED.indexOf(char) > -1) {
55
- const err = new TypeError(`Invalid query token at ${ i }: '${ char }'`);
56
- throw err;
57
- }
58
-
59
- if (char === '(') {
60
- // Error If there is nothing behind:
61
- if (param.length === 0) {
62
- const err = new TypeError(`Invalid query token at ${ i }: '${ char }'`);
63
- throw err;
64
- }
65
-
66
- // If not special token, go deeper:
67
- if (['and', 'or', 'xor', 'not', '!', '|', 'like'].indexOf(token) === -1) {
68
- this.append(param, token);
69
- deep++;
70
- }
71
-
72
- // will set ( in token later.
73
- }
74
-
75
- if (char === ')') {
76
- // If sub-level:
77
- if (deep > 0) {
78
- deep--
79
- }
80
- }
81
-
82
- // & can mean the end of key=value pair:
83
- if (char === '&') {
84
- // If top-level:
85
- if (deep === 0) {
86
- this.append(param, token);
87
- param = '';
88
- token = '';
89
- continue;
90
- }
91
-
92
- // If sub-level do nothing.
93
- }
94
-
95
- // = can mean the end of param name:
96
- if (char === '=') {
97
- // If top-level:
98
- if (deep === 0) {
99
- param = token;
100
- token = '';
101
- }
102
- }
103
-
104
- // Continue building token:
105
- if (char !== '=' || deep > 0 ) {
106
- token += char;
107
- }
108
-
109
- // If last char:
110
- if (i === decoded.length-1) {
111
- // Validate:
112
- if (deep > 0) {
113
- const err = new TypeError(`Missing ')' at ${ i }`);
114
- throw err;
115
- }
116
-
117
- this.append(param, token);
118
- }
119
- }
120
- }
121
-
122
- append(...args) {
123
- return this._map.set(...args);
124
- }
125
-
126
- get(...args) {
127
- return this._map.get(...args);
128
- }
129
-
130
- delete(...args) {
131
- return this._map.delete(...args);
132
- }
133
-
134
- entries(...args) {
135
- return this._map.entries(...args);
136
- }
137
-
138
- toString() {
139
- return this._map.toString();
140
- }
141
-
142
- [util.inspect.custom](depth, opts) {
143
- return this._map;
144
- }
145
- }
@@ -1,79 +0,0 @@
1
- const {
2
- splitByComma,
3
- splitByDot
4
- } = require('nodester/utils/strings.util');
5
- // Query util.
6
- const {
7
- hasSubIncludesQuery,
8
- cutSubIncludesQuery,
9
- parseSubIncludesQuery
10
- } = require('nodester/utils/queries.util');
11
-
12
-
13
- module.exports = {
14
- parseIncludesQuery: _parseIncludesQuery
15
- }
16
-
17
- function _parseIncludesQuery(
18
- modelDefinition,
19
- includesQueryString
20
- ) {
21
- const associationAndSubs = splitByComma(includesQueryString);
22
- const allowedAssociations = Object.keys(modelDefinition.associations);
23
-
24
- associationAndSubs.forEach((a) => {
25
- let association = a.split('.')[0];
26
-
27
- if (hasSubIncludesQuery(association)) {
28
- const [ nestedQuery, _association ] = cutSubIncludesQuery(association);
29
- association = _association;
30
- }
31
-
32
- if (allowedAssociations.indexOf(association) === -1) {
33
- const err = new Error(`Association with name "${ association }" doesn't exist on this model.`);
34
- err.name = 'NotFound';
35
- throw err;
36
- }
37
- });
38
-
39
- const result = associationAndSubs.map(associationAndSubsString => _parseSubIncludes(associationAndSubsString));
40
-
41
- return result;
42
- }
43
-
44
- function _parseSubIncludes(associationAndSubsString) {
45
- const associationAndSubs = splitByDot(associationAndSubsString);
46
-
47
- // If association has SubIncludes:
48
- if (associationAndSubs?.length > 0) {
49
- const clearAssociation = associationAndSubs[0];
50
- const subs = associationAndSubs.splice(1, associationAndSubs.length - 1);
51
-
52
- if (subs.length > 0) {
53
- const result = {
54
- association: clearAssociation,
55
- include: [ _parseSubIncludes(subs.join('.')) ]
56
- };
57
-
58
- _parseSubIncludesQueryIfPresent(result);
59
-
60
- return result;
61
- }
62
- }
63
-
64
- // By default just return association.
65
- const result = { association: associationAndSubsString };
66
- _parseSubIncludesQueryIfPresent(result);
67
- return result;
68
- }
69
-
70
- function _parseSubIncludesQueryIfPresent(resultQuery) {
71
- if (hasSubIncludesQuery(resultQuery.association)) {
72
- const [ nestedQuery, clearAssociation ] = cutSubIncludesQuery(resultQuery.association);
73
-
74
- resultQuery.association = clearAssociation;
75
- parseSubIncludesQuery(clearAssociation, nestedQuery, resultQuery);
76
- }
77
-
78
- return resultQuery;
79
- }
@@ -1,147 +0,0 @@
1
- // Format of token: "Authorization: Bearer [token]".
2
- const ACCESS_TOKEN_NAME = 'Authorization';
3
- const REFRESH_TOKEN_NAME = 'x-refresh-token';
4
- // JWT module.
5
- const jwt = require('jsonwebtoken');
6
- // Utils.
7
- const { addSeconds } = require('nodester/utils/dates.util');
8
-
9
-
10
- module.exports = class JWTService {
11
- constructor(
12
- accessTokenConfigs,
13
- refreshTokenConfigs
14
- ) {
15
- if (!accessTokenConfigs || !refreshTokenConfigs){
16
- throw new Error('"accessTokenConfigs" and "refreshTokenConfigs" are required arguments.');
17
- }
18
-
19
- this.accessTokenConfigs = { ...accessTokenConfigs };
20
- this.refreshTokenConfigs = { ...refreshTokenConfigs };
21
- }
22
-
23
- extractTokenFromRequest(request) {
24
- let token;
25
-
26
- if (request.header(ACCESS_TOKEN_NAME)) {
27
- token = this._parseAccessToken(
28
- request.header(ACCESS_TOKEN_NAME)
29
- );
30
- }
31
- else if (request.cookies[ACCESS_TOKEN_NAME]) {
32
- token = this._parseAccessToken(
33
- request.cookies[ACCESS_TOKEN_NAME]
34
- );
35
- }
36
- // Check token in query:
37
- else if (!!request.query.token) {
38
- token = request.query.token;
39
- delete request.body.token;
40
- }
41
- // Check token in body:
42
- else if (!!request.body.token) {
43
- token = request.body.token;
44
- delete request.query.token;
45
- }
46
- else {
47
- const err = new Error(`No ${ACCESS_TOKEN_NAME} was found`);
48
- err.name = 'NoToken';
49
- err.details = { message:err.message };
50
- throw err;
51
- }
52
-
53
- return token;
54
- }
55
-
56
- extractRefreshTokenFromRequest(request) {
57
- let token;
58
-
59
- if (request.header(REFRESH_TOKEN_NAME)) {
60
- token = request.header(REFRESH_TOKEN_NAME);
61
- }
62
- else if (request.cookies[REFRESH_TOKEN_NAME]) {
63
- token = request.cookies[REFRESH_TOKEN_NAME];
64
- }
65
- // Check token in query:
66
- else if (!!request.query.token) {
67
- token = request.query.token;
68
- delete request.body.token;
69
- }
70
- // Check token in body:
71
- else if (!!request.body.token) {
72
- token = request.body.token;
73
- delete request.query.token;
74
- }
75
- else {
76
- const err = new Error(`No ${REFRESH_TOKEN_NAME} was found`);
77
- err.name = 'NoToken';
78
- err.details = { message:err.message };
79
- throw err;
80
- }
81
-
82
- return token;
83
- }
84
-
85
- issueAccessToken(payload) {
86
- const { secret, expiresIn } = this.accessTokenConfigs;
87
- return this._issueToken({ payload, secret, expiresIn });
88
- }
89
-
90
- issueRefreshToken(payload) {
91
- const { secret, expiresIn } = this.refreshTokenConfigs;
92
- return this._issueToken({ payload, secret, expiresIn });
93
- }
94
-
95
- verifyAccessToken(token) {
96
- const { secret } = this.accessTokenConfigs;
97
- return this._verifyToken({ token, secret });
98
- }
99
-
100
- verifyRefreshToken(token) {
101
- const { secret } = this.refreshTokenConfigs;
102
- return this._verifyToken({ token, secret });
103
- }
104
-
105
- async _issueToken({ payload, secret, expiresIn }) {
106
- try {
107
- const token = jwt.sign(payload, secret, { expiresIn });
108
- const expirationDateValue = (addSeconds(new Date(), expiresIn/1000)).valueOf();
109
-
110
- const fullToken = { token, expiresIn, expirationDateValue };
111
- return Promise.resolve(fullToken);
112
- }
113
- catch(error) {
114
- return Promise.reject(error);
115
- }
116
- }
117
-
118
- async _verifyToken({ token, secret }) {
119
- try {
120
- const parsedToken = await jwt.verify(token, secret, {});
121
- return Promise.resolve(parsedToken);
122
- }
123
- catch(error) {
124
- const err = new Error('Invalid signature.');
125
- err.name = 'ValidationError';
126
- return Promise.reject(err);
127
- }
128
- }
129
-
130
- _parseAccessToken(token) {
131
- let parsed = '';
132
-
133
- const parts = token.split(' ');
134
-
135
- if (parts.length === 2 && /^Bearer$/.test(parts[0])) {
136
- parsed = parts[1];
137
- }
138
- else {
139
- const err = new Error(`Format for ${ACCESS_TOKEN_NAME}: Bearer [token]`);
140
- err.name = 'InvalidFormat';
141
- err.details = { message: err.message };
142
- throw err;
143
- }
144
-
145
- return parsed;
146
- }
147
- }
@@ -1,159 +0,0 @@
1
- const finalhandler = require('finalhandler');
2
- const consl = require('nodester/loggers/console');
3
- const debug = require('debug')('nodester:MiddlewareStack');
4
-
5
-
6
- module.exports = class MiddlewareStack {
7
- constructor(opts={}) {
8
- // This array MUST stay flat!
9
- this.middlewares = [];
10
-
11
- // Indicates whether we can add more middlewares or no.
12
- this.isLocked = false;
13
- this.finalhandlerEnabled = !!opts.finalhandlerEnabled;
14
-
15
-
16
- const env = process.env.NODE_ENV || 'development';
17
- // Final middleware & error handler.
18
- this.finalhandler = (req, res) => finalhandler(req, res, {
19
- env: env,
20
- onerror: consl.error.bind(this)
21
- });
22
- }
23
-
24
-
25
- /**
26
- * Add the given middleware `fn` to the stack.
27
- *
28
- * @param {Function} fn
29
- * @param {Integer} index (0 or undefined)
30
- * @return {Integer} index of new middleware
31
- *
32
- * @api public
33
- */
34
- add(fn, index) {
35
- if (this.isLocked) {
36
- const err = new Error(`Can't add more middlewares while stack is locked.`);
37
- throw err;
38
- }
39
-
40
- if (typeof fn !== 'function') {
41
- const err = new TypeError('middleware must be a function!');
42
- throw err;
43
- }
44
-
45
- if (!!index && isNaN(index)) {
46
- const err = new TypeError('"index" must be an Integer!');
47
- throw err;
48
- }
49
-
50
- let pushedIndex = -1;
51
-
52
- if (index === 0) {
53
- this.middlewares.unshift(fn);
54
- pushedIndex = 0;
55
- }
56
- else {
57
- pushedIndex = this.middlewares.push(fn);
58
- }
59
-
60
- debug(`added middleware (${ pushedIndex })`);
61
- return pushedIndex;
62
- }
63
-
64
-
65
- /**
66
- * Removes middleware at index.
67
- *
68
- * @param {Integer} index
69
- * @return {MiddlewareStack} self
70
- *
71
- * @api public
72
- */
73
- remove(index=-1) {
74
- if (this.isLocked) {
75
- const err = new Error(`Can't remove middlewares while stack is locked.`);
76
- throw err;
77
- }
78
-
79
- if (isNaN(index)) {
80
- const err = new TypeError('"index" must be an Integer!');
81
- throw err;
82
- }
83
-
84
- this.middlewares.splice(index, 1);
85
- debug(`removed middleware (${ index })`);
86
- return this;
87
- }
88
-
89
-
90
- /*
91
- * Prepare stack for processing.
92
- *
93
- * @api public
94
- */
95
- lock() {
96
- if (this.finalhandlerEnabled) {
97
- // Add final handler to the stack.
98
- this.add((req, res)=>this.finalhandler(req, res)());
99
- }
100
-
101
- // Stack is ready.
102
- this.isLocked = true;
103
-
104
- debug(`stack is locked`);
105
- }
106
-
107
-
108
- /*
109
- * Unlocks stack.
110
- *
111
- * @api public
112
- */
113
- unlock() {
114
- this.isLocked = false;
115
-
116
- if (this.finalhandlerEnabled) {
117
- this.middlewares.pop();
118
- }
119
-
120
- debug(`stack is unlocked`);
121
- }
122
-
123
-
124
- /*
125
- * Start chain.
126
- *
127
- * @api public
128
- */
129
- process(req, res, next) {
130
- let middlewareOffset = -1;
131
-
132
- const _next = (...args) => {
133
- middlewareOffset += 1;
134
- const fn = this.middlewares[middlewareOffset];
135
-
136
- try {
137
- if (!fn && !next) {
138
- const err = new TypeError(`Handler for ${ req.method } ${ req.url } is not defined.`);
139
- throw err;
140
- }
141
- else if (!fn && !!next) {
142
- return next.call(null, req, res, next, ...args);
143
- }
144
-
145
- return fn.call(null, req, res, _next, ...args);
146
- }
147
- catch(error) {
148
- return this.finalhandler(req, res)(error);
149
- }
150
- }
151
-
152
- return _next();
153
- }
154
-
155
-
156
- get length() {
157
- return this.middlewares.length;
158
- }
159
- }