nodester 0.0.1 → 0.0.2

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 (68) hide show
  1. package/Readme.md +33 -39
  2. package/lib/application/index.js +110 -38
  3. package/lib/constants/Operations.js +23 -0
  4. package/lib/controllers/methods/index.js +194 -0
  5. package/lib/controllers/mixins/index.js +222 -0
  6. package/lib/database/connection.js +34 -0
  7. package/lib/database/migration.js +42 -0
  8. package/lib/database/utils.js +19 -0
  9. package/lib/enums/Enum.js +16 -0
  10. package/lib/facades/methods/index.js +173 -0
  11. package/lib/facades/mixins/index.js +111 -0
  12. package/lib/factories/errors/CustomError.js +7 -5
  13. package/lib/factories/responses/html.js +7 -2
  14. package/lib/factories/responses/rest.js +110 -0
  15. package/lib/http/codes/index.js +157 -0
  16. package/lib/{application/http → http}/request.js +6 -30
  17. package/lib/{application/http → http}/response.js +20 -53
  18. package/lib/middlewares/etag/index.js +62 -0
  19. package/lib/middlewares/ql/sequelize/index.js +34 -0
  20. package/lib/middlewares/ql/sequelize/interpreter/ModelsTree.js +121 -0
  21. package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +456 -0
  22. package/lib/models/associate.js +17 -0
  23. package/lib/models/define.js +56 -14
  24. package/lib/models/mixins.js +100 -78
  25. package/lib/params/Params.js +37 -0
  26. package/lib/queries/Colander.js +84 -0
  27. package/lib/queries/NodesterQueryParams.js +139 -0
  28. package/lib/queries/traverse.js +311 -0
  29. package/lib/router/handlers.util.js +61 -0
  30. package/lib/router/index.js +386 -0
  31. package/lib/router/route.js +124 -0
  32. package/lib/router/routes.util.js +66 -0
  33. package/lib/stacks/MarkersStack.js +35 -0
  34. package/lib/{application → stacks}/MiddlewareStack.js +47 -13
  35. package/lib/utils/path.util.js +3 -1
  36. package/lib/utils/types.util.js +51 -1
  37. package/lib/validators/dates.js +25 -0
  38. package/lib/validators/numbers.js +14 -0
  39. package/package.json +31 -4
  40. package/tests/index.test.js +7 -2
  41. package/tests/nql.test.js +277 -0
  42. package/docs/App.md +0 -13
  43. package/docs/Queries.md +0 -61
  44. package/docs/Readme.md +0 -2
  45. package/docs/Routing.md +0 -34
  46. package/examples/goal/index.js +0 -23
  47. package/examples/rest/index.js +0 -25
  48. package/examples/rest/node_modules/.package-lock.json +0 -40
  49. package/examples/rest/package-lock.json +0 -72
  50. package/examples/rest/package.json +0 -14
  51. package/lib/constants/ConstantsEnum.js +0 -13
  52. package/lib/controllers/Controller.js +0 -474
  53. package/lib/controllers/JWTController.js +0 -240
  54. package/lib/controllers/ServiceController.js +0 -109
  55. package/lib/controllers/WebController.js +0 -75
  56. package/lib/facades/Facade.js +0 -388
  57. package/lib/facades/FacadeParams.js +0 -11
  58. package/lib/facades/ServiceFacade.js +0 -17
  59. package/lib/facades/jwt.facade.js +0 -273
  60. package/lib/factories/responses/api.js +0 -90
  61. package/lib/models/DisabledRefreshToken.js +0 -68
  62. package/lib/models/Extractor.js +0 -320
  63. package/lib/routers/Default/index.js +0 -143
  64. package/lib/routers/Default/layer.js +0 -50
  65. package/lib/routers/Main/index.js +0 -10
  66. package/lib/routers/Roles/index.js +0 -81
  67. package/lib/utils/params.util.js +0 -19
  68. /package/lib/{application/http → http}/utils.js +0 -0
@@ -0,0 +1,386 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const MiddlewareStack = require('../stacks/MiddlewareStack');
9
+ const MarkersStack = require('../stacks/MarkersStack');
10
+ const Route = require('./route');
11
+ // Utils:
12
+ const { typeOf } = require('../utils/types.util');
13
+ const { wrapRouteHandler } = require('./routes.util');
14
+ // File system:
15
+ const Path = require('path');
16
+ const fs = require('fs');
17
+ const commonExtensions = require('common-js-file-extensions');
18
+ // Debug & console:
19
+ const consl = require('../logger/console');
20
+ const debug = require('debug')('nodester:router');
21
+
22
+
23
+ module.exports = class NodesterRouter {
24
+
25
+ /**
26
+ * Initialize a new `NodesterRouter`.
27
+ *
28
+ * @param {Object} opts
29
+ * - {Array} codeFileExtensions
30
+ * - {String} controllersPath
31
+ * - {Object} controllers
32
+ * - {Boolean} finalhandlerEnabled
33
+ *
34
+ * @api public
35
+ */
36
+ constructor(opts={}) {
37
+ // Reference to middlewares stack.
38
+ this._middlewares = new MiddlewareStack({ finalhandlerEnabled: !!opts.finalhandlerEnabled });
39
+
40
+ // Reference to the markers stack.
41
+ this._markers = new MarkersStack();
42
+
43
+ // Reference to the controllers stack.
44
+ this._controllers = new Map();
45
+
46
+ this.codeFileExtensions = commonExtensions.code;
47
+ this.paths = {};
48
+
49
+ // Indicates whether we can add more middlewares or no.
50
+ this.isLocked = false;
51
+
52
+ // Options:
53
+ // if "codeFileExtensions" was set:
54
+ if (!!opts.codeFileExtensions && Array.isArray(opts.codeFileExtensions)) {
55
+ this.codeFileExtensions = [...opts.codeFileExtensions];
56
+ }
57
+
58
+ // If "controllersPath" was set, cache all controllers in directory:
59
+ if (!!opts.controllersPath) {
60
+ // Save path.
61
+ this.paths.controllers = opts.controllersPath;
62
+
63
+ const availableFileExtensions = this.codeFileExtensions;
64
+ // Only get files, which have available file extensions:
65
+ const fileNames = fs.readdirSync(this.paths.controllers);
66
+ for (const fileName of fileNames) {
67
+
68
+ const nameParts = fileName.split('.');
69
+ const extension = nameParts.pop();
70
+ if (availableFileExtensions.indexOf(extension) === -1) {
71
+ continue;
72
+ }
73
+
74
+ // If the name format is <model>.<controller>,
75
+ // but second part is not "controller":
76
+ if (nameParts.length > 1 && nameParts[1] !== 'controller')
77
+ continue;
78
+
79
+ const controller = require(Path.join(this.paths.controllers, fileName));
80
+ const controllerName = nameParts[0];
81
+ this.addController(controller, controllerName);
82
+ }
83
+ }
84
+
85
+ // If "controllers" were provided as an Object:
86
+ if (!!opts.controllers) {
87
+ if (typeOf(opts.controllers) !== 'Object') {
88
+ const err = new TypeError(`"controllers" must be an Object.`);
89
+ throw err;
90
+ }
91
+
92
+ const entities = Object.entities(opts.controllers);
93
+ for (const [controllerName, controllerDefinition] of entities) {
94
+ this.addController(controllerDefinition, controllerName);
95
+ }
96
+ }
97
+ }
98
+
99
+
100
+ /*
101
+ * Adds:
102
+ * - (controller) new controller to the stack;
103
+ * - (middleware) new middleware to the stack;
104
+ * - (marker) new marker to the stack;
105
+ * - (route) new route to the stack;
106
+ *
107
+ * @api public
108
+ */
109
+ get add() {
110
+ return {
111
+ controller: this.addController.bind(this),
112
+ middleware: this.addMiddleware.bind(this),
113
+ marker: this.addMarker.bind(this),
114
+ route: this.addRoute.bind(this),
115
+ routes: this.addRoutes.bind(this),
116
+ }
117
+ }
118
+
119
+
120
+ /*
121
+ * Adds new controller to the controllers stack.
122
+ *
123
+ * @param {Function|Object} controller
124
+ * @param {String} controllerName
125
+ *
126
+ * @api public
127
+ */
128
+ addController(fnOrObject, controllerName=null) {
129
+ const controllerType = typeOf(fnOrObject);
130
+ const name = controllerName ?? fnOrObject?.name ?? fnOrObject.constructor.name;
131
+
132
+ // If controller was exported as Object:
133
+ if (controllerType === 'Object') {
134
+ this._controllers.set(name, fnOrObject);
135
+ }
136
+ // If controller was exported as a constructor function:
137
+ else if (controllerType === 'function') {
138
+ this._controllers.set(name, new fnOrObject());
139
+ }
140
+ else {
141
+ const err = new TypeError(`Please check how you exported ${ name }, it should be either Object or constructor function.`);
142
+ throw err;
143
+ }
144
+ }
145
+
146
+
147
+ /*
148
+ * Adds new middleware to the stack.
149
+ *
150
+ * @param {Function} fn
151
+ * @param {Integer} index (0 or undefined)
152
+ *
153
+ * @return {Integer} index of new middleware
154
+ *
155
+ * @api public
156
+ */
157
+ addMiddleware(fn, index) {
158
+ return this._middlewares.add(fn, index);
159
+ }
160
+
161
+
162
+ /*
163
+ * Adds new marker to the stack.
164
+ *
165
+ * @param {String} markerName
166
+ * @param {Function} fn
167
+ *
168
+ * @api public
169
+ */
170
+ addMarker(markerName='', fn) {
171
+ if (markerName.length > 0 && markerName.indexOf('/') > -1) {
172
+ const err = new TypeError(`"markerName" can't contain slashes "/"`);
173
+ throw err;
174
+ }
175
+
176
+ if (this._markers.get(markerName)) {
177
+ const err = new Error(`Marker with name ${ markerName } is already set.`);
178
+ throw err;
179
+ }
180
+
181
+ this._markers.add(markerName, fn);
182
+ }
183
+
184
+
185
+ /*
186
+ * Creates route middleware and adds it to the stack.
187
+ *
188
+ * @param {String} route
189
+ * @param {Object|Function} handler
190
+ *
191
+ * @api public
192
+ */
193
+ addRoute(route='', handler) {
194
+ const parsed = new Route(route);
195
+ const handlerType = typeOf(handler);
196
+
197
+ // ToDo: move it to separate validator:
198
+ if (parsed.method === undefined) {
199
+ const err = new TypeError(`"route" should start with one of the following methods: [GET, POST, PUT, DELETE, QUERY, HEADER, OPTIONS]`);
200
+ throw err;
201
+ }
202
+
203
+ if (handlerType === 'Object' && !this.paths.controllers) {
204
+ const err = new TypeError(`Please set "controllersPath" during Router initialization.`);
205
+ throw err;
206
+ }
207
+
208
+ const wrapped = wrapRouteHandler.call(this, parsed, handler);
209
+ return this.addMiddleware(wrapped);
210
+ }
211
+
212
+
213
+ /*
214
+ * Loops through provided "routes" object
215
+ * and adds them throught "addRoute".
216
+ *
217
+ * @param {Object} routes
218
+ *
219
+ * @api public
220
+ */
221
+ addRoutes(routes={}) {
222
+ for (const [route, handler] of Object.entries(routes)) {
223
+ this.addRoute(route, handler);
224
+ }
225
+ }
226
+
227
+
228
+ /*
229
+ * Proxy to .add.middleware()
230
+ *
231
+ * @param {Function} fn
232
+ *
233
+ * @api public
234
+ */
235
+ use(fn) {
236
+ return this.add.middleware(fn);
237
+ }
238
+
239
+
240
+ /*
241
+ * Adds middlewares, which only fires for set marker.
242
+ *
243
+ * @param {String} markerName
244
+ *
245
+ * @api public
246
+ */
247
+ only(markerName='') {
248
+ const self = this;
249
+ const markerFn = this._markers.get(markerName);
250
+
251
+ return {
252
+ route: (route, fn) => {
253
+ const parsed = new Route(route);
254
+ // ToDo: move it to separate validator:
255
+ if (parsed.method === undefined) {
256
+ const err = new TypeError(`"route" should start with one of the following methods: [GET, POST, PUT, DELETE, QUERY, HEADER, OPTIONS]`);
257
+ throw err;
258
+ }
259
+
260
+ const wrapped = async (req, res, next) => {
261
+ const matched = await markerFn.call(self, req, res);
262
+ // Skip, if marker's condition was not matched:
263
+ if (!matched) {
264
+ return next();
265
+ }
266
+
267
+ // Wrap and call:
268
+ const routeHandler = wrapRouteHandler.call(self, parsed, fn);
269
+ await routeHandler.call(self, req, res, next);
270
+
271
+ // If response was not sent,
272
+ // go to next one:
273
+ if (res.headersSent === false) {
274
+ next();
275
+ }
276
+ };
277
+ return self.add.route(wrapped);
278
+ },
279
+ use: (fn) => {
280
+ const wrapped = async (req, res, next) => {
281
+ const matched = await markerFn.call(self, req, res);
282
+ // Skip, if marker's condition was not matched:
283
+ if (!matched) {
284
+ return next();
285
+ }
286
+
287
+ await fn.call(self, req, res, next);
288
+
289
+ // If response was not sent,
290
+ // go to next one:
291
+ if (res.headersSent === false) {
292
+ next();
293
+ }
294
+ };
295
+ return self.use(wrapped);
296
+ }
297
+ }
298
+ }
299
+
300
+
301
+ /**
302
+ * Handles server request.
303
+ *
304
+ * @api public
305
+ */
306
+ handle(req, res, next) {
307
+ return this._middlewares.process(req, res, next)
308
+ }
309
+
310
+
311
+ /*
312
+ * Prepare router for processing.
313
+ *
314
+ * @api public
315
+ */
316
+ lock() {
317
+ // Stack is ready.
318
+ this.isLocked = true;
319
+
320
+ debug(`router is locked`);
321
+
322
+ this._middlewares.lock();
323
+ }
324
+
325
+
326
+ /*
327
+ * Unlocks router.
328
+ *
329
+ * @api public
330
+ */
331
+ unlock() {
332
+ this.isLocked = false;
333
+
334
+ debug(`router is unlocked`);
335
+
336
+ this._middlewares.unlock();
337
+ }
338
+
339
+
340
+ /*
341
+ * Removes something
342
+ */
343
+ get remove() {
344
+ return {
345
+ middleware: this.removeMiddleware.bind(this)
346
+ }
347
+ }
348
+
349
+
350
+ /**
351
+ * Removes middleware at index.
352
+ *
353
+ * @param {Integer} index
354
+ *
355
+ * @return {Integer} middlewares stack length
356
+ *
357
+ * @api public
358
+ */
359
+ removeMiddleware(index=-1) {
360
+ this._middlewares.remove(index);
361
+ return this._middlewares.length;
362
+ }
363
+
364
+
365
+
366
+ /**
367
+ * Extends Router & makes sure, that "key" param is not present already.
368
+ *
369
+ * @param {String} key
370
+ * @param {Any} fnOrProperty
371
+ * @return {Any} fnOrProperty in Router
372
+ *
373
+ * @api public
374
+ */
375
+ extend(key='', fnOrProperty) {
376
+ const keys = Object.keys(this);
377
+ if (keys.indexOf(key) > -1) {
378
+ const err = new TypeError(`Key ${ key } is already present in Router instance`);
379
+ throw err;
380
+ }
381
+
382
+ this[key] = fnOrProperty;
383
+
384
+ return this[key];
385
+ }
386
+ }
@@ -0,0 +1,124 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ // Utils.
9
+ const { typeOf } = require('../utils/types.util');
10
+
11
+
12
+ module.exports = class NodesterRoute {
13
+
14
+ /**
15
+ * Initialize a new `NodesterRoute`.
16
+ *
17
+ * @param {Object|String} routeStringOrOpts
18
+ * - {String} method
19
+ * - {String} route
20
+ * - {Array} parts
21
+ * - {Object} params
22
+ *
23
+ * @api public
24
+ */
25
+ constructor(routeStringOrOpts={}) {
26
+ if (typeOf(routeStringOrOpts) === 'Object') {
27
+ this.method = opts.method ?? undefined;
28
+ this.route = opts.route ?? null;
29
+ this.parts = opts.parts ?? [];
30
+ this.params = opts.params ?? {};
31
+ }
32
+ else if (typeOf(routeStringOrOpts) === 'string'){
33
+ this.method = undefined;
34
+ this.route = null;
35
+ this.parts = [];
36
+ this.params = {};
37
+
38
+ if (routeStringOrOpts.length === 0) {
39
+ return this;
40
+ }
41
+
42
+ // Parse:
43
+ const parts = routeStringOrOpts.split(' ');
44
+ const cleared = parts.filter(p => p.length > 0);
45
+
46
+ // Set method:
47
+ if (cleared[0].indexOf('/') === -1) {
48
+ const method = cleared.shift().toUpperCase();
49
+ this.method = method;
50
+ }
51
+
52
+ // Build route one again and set it:
53
+ const clearRoute = cleared.join('');
54
+ this.route = clearRoute;
55
+
56
+ // Parse path parts:
57
+ const pathParts = clearRoute.split('/')
58
+ .filter(p => p.length > 0);
59
+ this.parts = pathParts;
60
+ }
61
+ }
62
+
63
+
64
+ /**
65
+ * @param {String} routeToTest
66
+ *
67
+ * @return {Boolean}
68
+ *
69
+ * @alias matches
70
+ * @private
71
+ */
72
+ matches(routeToTest='') {
73
+ const {
74
+ route,
75
+ parts,
76
+ params
77
+ } = this;
78
+
79
+ if (routeToTest === route) {
80
+ return true;
81
+ }
82
+
83
+ // Break route & test:
84
+ const testParts = routeToTest.split('/')
85
+ .filter(p => p.length > 0);
86
+ let matched = true;
87
+ if (testParts.length === 0) {
88
+ matched = route === '/';
89
+ }
90
+ else {
91
+ for (let i=0; i < testParts.length; i++) {
92
+ const part = parts[i];
93
+ const testPart = testParts[i];
94
+
95
+ // If route is shorter:
96
+ if (!part) {
97
+ matched = false;
98
+ break;
99
+ }
100
+
101
+ // If part is wild:
102
+ if (part === '*') {
103
+ matched = true;
104
+ this.params.pathParts = testParts.slice(i);
105
+ break;
106
+ }
107
+
108
+ // If part is param:
109
+ if (part[0] === ':') {
110
+ this.params[part.split(':')[1]] = testPart;
111
+ continue;
112
+ }
113
+
114
+ // If not matched:
115
+ if (part !== testPart) {
116
+ matched = false;
117
+ break;
118
+ }
119
+ }
120
+ }
121
+
122
+ return matched;
123
+ }
124
+ }
@@ -0,0 +1,66 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ // Utils:
9
+ const { typeOf } = require('../utils/types.util');
10
+ const { parseRouteHandler } = require('./handlers.util');
11
+
12
+
13
+ module.exports = {
14
+ wrapRouteHandler: _wrapRouteHandler
15
+ }
16
+
17
+
18
+ /**
19
+ * Wraps route handler.
20
+ *
21
+ * @param {Route} routeInstance
22
+ * @param {Function} handler
23
+ *
24
+ * @alias wrapRouteHandler
25
+ * @api public
26
+ */
27
+ function _wrapRouteHandler(routeInstance, handler) {
28
+ const handlerType = typeOf(handler);
29
+
30
+ const wrapped = async (req, res, next) => {
31
+ // Get route without SearchParams.
32
+ const route = req.url.split('?')[0];
33
+
34
+ // If route not matched:
35
+ const methodMatched = routeInstance.method === req.method;
36
+ const routeMatched = routeInstance.matches(route);
37
+ if (!methodMatched || !routeMatched) {
38
+ return next();
39
+ }
40
+
41
+ // Populate params.
42
+ req.params = routeInstance.params;
43
+
44
+ if (handlerType === 'function') {
45
+ await handler.call(this, req, res, next);
46
+ }
47
+ // If handler is an Object:
48
+ else {
49
+ const parsedHandler = parseRouteHandler(handler);
50
+
51
+ // Get provided method from controller:
52
+ const controller = this._controllers.get(parsedHandler.controllerName);
53
+ const controllerAction = controller[parsedHandler.actionName];
54
+
55
+ // If User set any middleware before, call it first:
56
+ if (typeOf(parsedHandler.before) === 'function') {
57
+ // Expose nquery first.
58
+ await parsedHandler.before(req.nquery, req, res);
59
+ }
60
+
61
+ await controllerAction(req, res);
62
+ }
63
+ };
64
+
65
+ return wrapped;
66
+ }
@@ -0,0 +1,35 @@
1
+ const consl = require('../logger/console');
2
+ const debug = require('debug')('nodester:MiddlewareStack');
3
+
4
+
5
+ module.exports = class MarkersStack {
6
+ constructor() {
7
+ this.markers = new Map();
8
+ }
9
+
10
+
11
+ /**
12
+ * Add the given middleware `fn` to the stack.
13
+ *
14
+ * @param {String} markerName
15
+ * @param {Function} fn
16
+ * @return {Integer} index of new middleware
17
+ *
18
+ * @api public
19
+ */
20
+ add(markerName='', fn) {
21
+ return this.markers.set(markerName, fn);
22
+ }
23
+
24
+
25
+ /**
26
+ * Get middleware function by marker name.
27
+ *
28
+ * @param {String} markerName
29
+ *
30
+ * @api public
31
+ */
32
+ get(markerName='') {
33
+ return this.markers.get(markerName);
34
+ }
35
+ }