nodester 0.0.1

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/LICENSE +21 -0
  2. package/Readme.md +125 -0
  3. package/docs/App.md +13 -0
  4. package/docs/Queries.md +61 -0
  5. package/docs/Readme.md +2 -0
  6. package/docs/Routing.md +34 -0
  7. package/examples/goal/index.js +23 -0
  8. package/examples/rest/index.js +25 -0
  9. package/examples/rest/node_modules/.package-lock.json +40 -0
  10. package/examples/rest/package-lock.json +72 -0
  11. package/examples/rest/package.json +14 -0
  12. package/lib/application/MiddlewareStack.js +125 -0
  13. package/lib/application/http/request.js +462 -0
  14. package/lib/application/http/response.js +1107 -0
  15. package/lib/application/http/utils.js +254 -0
  16. package/lib/application/index.js +292 -0
  17. package/lib/constants/ConstantsEnum.js +13 -0
  18. package/lib/constants/ResponseFormats.js +7 -0
  19. package/lib/controllers/Controller.js +474 -0
  20. package/lib/controllers/JWTController.js +240 -0
  21. package/lib/controllers/ServiceController.js +109 -0
  22. package/lib/controllers/WebController.js +75 -0
  23. package/lib/facades/Facade.js +388 -0
  24. package/lib/facades/FacadeParams.js +11 -0
  25. package/lib/facades/ServiceFacade.js +17 -0
  26. package/lib/facades/jwt.facade.js +273 -0
  27. package/lib/factories/errors/CustomError.js +22 -0
  28. package/lib/factories/errors/index.js +9 -0
  29. package/lib/factories/responses/api.js +90 -0
  30. package/lib/factories/responses/html.js +55 -0
  31. package/lib/logger/console.js +24 -0
  32. package/lib/models/DisabledRefreshToken.js +68 -0
  33. package/lib/models/Extractor.js +320 -0
  34. package/lib/models/define.js +62 -0
  35. package/lib/models/mixins.js +369 -0
  36. package/lib/policies/Role.js +77 -0
  37. package/lib/policies/RoleExtracting.js +97 -0
  38. package/lib/preprocessors/BodyPreprocessor.js +61 -0
  39. package/lib/preprocessors/IncludesPreprocessor.js +55 -0
  40. package/lib/preprocessors/QueryPreprocessor.js +64 -0
  41. package/lib/routers/Default/index.js +143 -0
  42. package/lib/routers/Default/layer.js +50 -0
  43. package/lib/routers/Main/index.js +10 -0
  44. package/lib/routers/Roles/index.js +81 -0
  45. package/lib/services/includes.service.js +79 -0
  46. package/lib/services/jwt.service.js +147 -0
  47. package/lib/tools/sql.tool.js +82 -0
  48. package/lib/utils/dates.util.js +23 -0
  49. package/lib/utils/forms.util.js +22 -0
  50. package/lib/utils/json.util.js +49 -0
  51. package/lib/utils/mappers/Routes/index.js +100 -0
  52. package/lib/utils/mappers/Routes/utils.js +20 -0
  53. package/lib/utils/modelAssociations.util.js +44 -0
  54. package/lib/utils/objects.util.js +69 -0
  55. package/lib/utils/params.util.js +19 -0
  56. package/lib/utils/path.util.js +26 -0
  57. package/lib/utils/queries.util.js +240 -0
  58. package/lib/utils/sanitizations.util.js +111 -0
  59. package/lib/utils/sql.util.js +78 -0
  60. package/lib/utils/strings.util.js +43 -0
  61. package/lib/utils/types.util.js +26 -0
  62. package/package.json +63 -0
  63. package/tests/index.test.js +35 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Mark Khramko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/Readme.md ADDED
@@ -0,0 +1,125 @@
1
+ # nodester
2
+ > A robust boilerplate framework that makes iterative development easy.
3
+
4
+ ## Table of Contents
5
+
6
+ - [Usage](#usage)
7
+ - [Extending](#extending-application-functionality)
8
+ - [Philosophy](#philosophy)
9
+ - [License](#license)
10
+ - [Copyright](#copyright)
11
+
12
+ ## Usage
13
+
14
+ ```js
15
+ const nodester = require('nodester');
16
+ const db = require('#db');
17
+
18
+ const app = new nodester();
19
+ app.set.database(db);
20
+
21
+ app.listen(8080, function() {
22
+ console.log('listening on port', app.port);
23
+ });
24
+ ```
25
+
26
+ ## Markers
27
+
28
+ Marker is a functional condition that returns `true | false`, based on data in request.
29
+
30
+
31
+ ### Adding a Marker
32
+
33
+ ```js
34
+ app.add.marker('admin', (req) => req.role === 'admin');
35
+ ```
36
+
37
+
38
+ ### Using a Marker
39
+
40
+ ```js
41
+ app.only('admin').route('get /payments', <handler>);
42
+ app.only('/api').route('get /payments', <handler>);
43
+ ```
44
+
45
+
46
+ ## Router
47
+
48
+ ```js
49
+ app.route('get /books', function(req, res) { ... } );
50
+ app.route('get /books', { controlledBy: 'BooksController.getMany' } );
51
+ app.route('get /books/:id', { controlledBy: 'BooksController.getOne' } );
52
+ ```
53
+
54
+
55
+ ## Extending Application functionality
56
+
57
+
58
+ ### Extending instance (safe way):
59
+
60
+ ```js
61
+ const serveStatic = require('serve-static');
62
+
63
+ const nodester = require('nodester');
64
+
65
+ const app = nodester();
66
+ app.extend('static', serveStatic);
67
+ app.static(<path_to_static_directory>);
68
+ ```
69
+
70
+ Short:
71
+ ```js
72
+ const serveStatic = require('serve-static');
73
+
74
+ const nodester = require('nodester');
75
+
76
+ const app = nodester();
77
+ app.extend('static', serveStatic)(<path_to_static_directory>);
78
+ ```
79
+
80
+ Of course you might just do this:
81
+ ```js
82
+ const serveStatic = require('serve-static');
83
+
84
+ const nodester = require('nodester');
85
+
86
+ const app = nodester();
87
+ app.static = serveStatic;
88
+ ````
89
+ But you'll never know if you did override any of the app's properties or did not.
90
+
91
+
92
+ ### Extending class:
93
+
94
+ If you really want to override properties or use `nodester` as a boilerplate, you should better extend default Application class:
95
+
96
+ ```js
97
+ const DefaultApplication = require('nodester');
98
+
99
+ class MyApp extends DefaultApplication {
100
+ constructor(opts) {
101
+ super(opts)
102
+ }
103
+
104
+ // Override everything you want here...
105
+ }
106
+
107
+ // Don't forget to expose.
108
+ module.exports = MyApp;
109
+ ```
110
+
111
+ ## Philosophy
112
+
113
+ The Philosophy of `nodester` is to provide a developer with a tool that can build an app (or feature) in hours and scale it with ease for years.
114
+
115
+ ### Goal
116
+
117
+ The goal of `nodester` is to be a robust boilerplate that makes iterative development easy.
118
+
119
+
120
+ ## LICENSE
121
+
122
+ MIT
123
+
124
+ ## Copyright
125
+ Copyright 2021-present [Mark Khramko](https://github.com/MarkKhramko)
package/docs/App.md ADDED
@@ -0,0 +1,13 @@
1
+ # nodester Application
2
+
3
+ ## Init
4
+
5
+ ```js
6
+ const nodester = require('nodester');
7
+
8
+ const app = new nodester();
9
+
10
+ app.listen(8080, function() {
11
+ console.log('listening on port', app.port);
12
+ });
13
+ ```
@@ -0,0 +1,61 @@
1
+ # nodester Queries API
2
+
3
+ ## Like value
4
+
5
+ To emulate MySQL's `like %value% ` query in URL,
6
+ pass `?key=like(value)` in the query.
7
+
8
+ * Example:
9
+ `http://localhost:5001/api/v1/countries?name=like(Engl)`
10
+
11
+
12
+ ## NotLike value
13
+
14
+ * Example:
15
+ `http://localhost:5001/api/v1/countries?name=notLike(Engl)`
16
+
17
+
18
+ ## Or
19
+
20
+ To emulate MySQL's `where key=value or key=value` query in URL,
21
+ pass `?key=or(value1,value2)` in the query.
22
+ * ! Note: don't use `spaces` between values.
23
+
24
+ * Example:
25
+ `http://localhost:5001/api/v1/countries?name=or(England,Germany)`
26
+
27
+
28
+ ## Count
29
+
30
+ MySQL's `select count(value)` query is run by default in facade's `getMany` function
31
+
32
+ * Response Example:
33
+ ```JSON
34
+ {
35
+ "count": 10,
36
+ "countries": [ ... ],
37
+ "limit": 10,
38
+ "skip": 0,
39
+ "total_count": 195
40
+ }
41
+ ```
42
+
43
+ ## Order (Sorting)
44
+
45
+
46
+ #### Top level
47
+
48
+ `order_by` & `order` arguments can be set in `query`
49
+ `http://localhost:5001/api/v1/countries?order_by=id&order=desc`
50
+
51
+ Above `query` will sort Countries[] by it's id.
52
+
53
+
54
+ #### Nested (Includes)
55
+
56
+ `http://localhost:5001/api/v1/countries?includes=cities(order_by=id&order=desc)`
57
+
58
+ Above `query` will sort Cities[] by it's id inside every Country object.
59
+
60
+ It can also do this:
61
+ `http://localhost:5001/api/v1/countries?includes=cities(order_by=id&order=desc).areas`
package/docs/Readme.md ADDED
@@ -0,0 +1,2 @@
1
+ # nodester API description
2
+
@@ -0,0 +1,34 @@
1
+ # nodester Routing
2
+
3
+ 1) Extend default `nodester` router
4
+ ```js
5
+ const Router = require('nodester/router');
6
+
7
+ class apiRouter extends Router {
8
+ constructor() {
9
+ super(opts);
10
+ this.add.controllers(<path_to_your_controllers>);
11
+ this.add.controllers(<path_to_your_controllers>, { namespace: 'view' });
12
+
13
+ this.add.routes()
14
+ }
15
+ }
16
+ ```
17
+
18
+ Root namespace is named `__root`
19
+
20
+ 2) Define routes
21
+ ```js
22
+ module.exports = {
23
+ 'GET /posts': { controlledBy: 'PostsController.getMany' },
24
+
25
+ // For different namespace:
26
+ 'GET /view/posts': { controlledBy: '@view PostsViewController.getMany' },
27
+ // Can also be written as:
28
+ 'GET /view/posts': {
29
+ namespace: 'view',
30
+ controller: 'PostsViewController'
31
+ action: 'getMany'
32
+ },
33
+ }
34
+ ```
@@ -0,0 +1,23 @@
1
+ const nodester = require('nodester');
2
+ const db = require('#db');
3
+
4
+ // Init.
5
+ const app = new nodester();
6
+
7
+
8
+ app.set.database(db);
9
+
10
+ app.extend('static')
11
+
12
+ app.static(<path/>);
13
+
14
+ // Markers.
15
+ app.add.marker('admin', (req)=>req.token.role === 'admin');
16
+
17
+ // Using Markers.
18
+ app.only('admin')
19
+ .route('get /payments', (req, res, next) => {});
20
+
21
+ app.listen(8080, function() {
22
+ console.log('listening on port', app.port);
23
+ });
@@ -0,0 +1,25 @@
1
+ const Nodester = require('nodester');
2
+
3
+ // Init.
4
+ const app = new Nodester();
5
+
6
+ // app.setDatabase();
7
+ // app.set('database');
8
+
9
+ // app.add('marker', 'GET_M', (req)=>req.method === 'GET');
10
+
11
+ app.add.middleware((req, res, next)=>{
12
+ console.log('1st');
13
+
14
+ res.setHeader("Content-type", "text/html");
15
+ res.write("Hello!<br/>...my friend");
16
+ res.end();
17
+ })
18
+
19
+ app.beforeStart(()=>{
20
+ console.log('Before start passed!');
21
+ });
22
+
23
+ app.listen(8080, function() {
24
+ console.log('listening on port', app.port);
25
+ });
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "nodester-example-rest",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 2,
5
+ "requires": true,
6
+ "packages": {
7
+ "../..": {
8
+ "version": "0.6.1",
9
+ "license": "MIT",
10
+ "dependencies": {
11
+ "accepts": "^1.3.8",
12
+ "content-disposition": "^0.5.4",
13
+ "content-type": "^1.0.5",
14
+ "cookie": "^0.5.0",
15
+ "cookie-signature": "^1.2.0",
16
+ "debug": "^4.3.4",
17
+ "finalhandler": "^1.2.0",
18
+ "formidable": "^1.2.6",
19
+ "inflection": "^2.0.1",
20
+ "proxy-addr": "^2.0.7",
21
+ "qs": "^6.11.0",
22
+ "sequelize": "^6.6.5",
23
+ "serve-static": "^1.15.0",
24
+ "slugify": "^1.6.5",
25
+ "type-is": "^1.6.18",
26
+ "vary": "^1.1.2"
27
+ },
28
+ "devDependencies": {
29
+ "jest": "^29.4.2"
30
+ },
31
+ "engines": {
32
+ "node": ">= 12.17.0"
33
+ }
34
+ },
35
+ "node_modules/nodester": {
36
+ "resolved": "../..",
37
+ "link": true
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "nodester-example-rest",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 2,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "nodester-example-rest",
9
+ "version": "1.0.0",
10
+ "license": "MIT",
11
+ "dependencies": {
12
+ "nodester": "file:../../"
13
+ }
14
+ },
15
+ "../..": {
16
+ "version": "0.6.1",
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "accepts": "^1.3.8",
20
+ "content-disposition": "^0.5.4",
21
+ "content-type": "^1.0.5",
22
+ "cookie": "^0.5.0",
23
+ "cookie-signature": "^1.2.0",
24
+ "debug": "^4.3.4",
25
+ "finalhandler": "^1.2.0",
26
+ "formidable": "^1.2.6",
27
+ "inflection": "^2.0.1",
28
+ "proxy-addr": "^2.0.7",
29
+ "qs": "^6.11.0",
30
+ "sequelize": "^6.6.5",
31
+ "serve-static": "^1.15.0",
32
+ "slugify": "^1.6.5",
33
+ "type-is": "^1.6.18",
34
+ "vary": "^1.1.2"
35
+ },
36
+ "devDependencies": {
37
+ "jest": "^29.4.2"
38
+ },
39
+ "engines": {
40
+ "node": ">= 12.17.0"
41
+ }
42
+ },
43
+ "node_modules/nodester": {
44
+ "resolved": "../..",
45
+ "link": true
46
+ }
47
+ },
48
+ "dependencies": {
49
+ "nodester": {
50
+ "version": "file:../..",
51
+ "requires": {
52
+ "accepts": "^1.3.8",
53
+ "content-disposition": "^0.5.4",
54
+ "content-type": "^1.0.5",
55
+ "cookie": "^0.5.0",
56
+ "cookie-signature": "^1.2.0",
57
+ "debug": "^4.3.4",
58
+ "finalhandler": "^1.2.0",
59
+ "formidable": "^1.2.6",
60
+ "inflection": "^2.0.1",
61
+ "jest": "^29.4.2",
62
+ "proxy-addr": "^2.0.7",
63
+ "qs": "^6.11.0",
64
+ "sequelize": "^6.6.5",
65
+ "serve-static": "^1.15.0",
66
+ "slugify": "^1.6.5",
67
+ "type-is": "^1.6.18",
68
+ "vary": "^1.1.2"
69
+ }
70
+ }
71
+ }
72
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "nodester-example-rest",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "author": "Mark Khramko",
10
+ "license": "MIT",
11
+ "dependencies": {
12
+ "nodester": "file:../../"
13
+ }
14
+ }
@@ -0,0 +1,125 @@
1
+ const finalhandler = require('finalhandler');
2
+ const consl = require('../logger/console');
3
+ const debug = require('debug')('nodester:MiddlewareStack');
4
+
5
+
6
+ module.exports = class MiddlewareStack {
7
+ constructor() {
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
+
14
+
15
+ const env = process.env.NODE_ENV || 'development';
16
+ // Final middleware & error handler.
17
+ this.finalhandler = (req, res) => finalhandler(req, res, {
18
+ env: env,
19
+ onerror: consl.error.bind(this)
20
+ });
21
+ }
22
+
23
+
24
+ /**
25
+ * Add the given middleware `fn` to the stack.
26
+ *
27
+ * @param {Function} fn
28
+ * @return {Integer} index of new middleware
29
+ *
30
+ * @api public
31
+ */
32
+ add(fn) {
33
+ if (this.isLocked) {
34
+ const err = new Error(`Can't add more middlewares while stack is locked.`);
35
+ throw err;
36
+ }
37
+
38
+ if (typeof fn !== 'function') {
39
+ const err = new TypeError('middleware must be a function!');
40
+ throw err;
41
+ }
42
+
43
+ const index = this.middlewares.push(fn);
44
+ debug(`added middleware (${ index })`);
45
+ return index;
46
+ }
47
+
48
+
49
+ /**
50
+ * Removes middleware at index.
51
+ *
52
+ * @param {Integer} index
53
+ * @return {MiddlewareStack} self
54
+ *
55
+ * @api public
56
+ */
57
+ remove(index=-1) {
58
+ if (this.isLocked) {
59
+ const err = new Error(`Can't remove middlewares while stack is locked.`);
60
+ throw err;
61
+ }
62
+
63
+ if (isNaN(index)) {
64
+ const err = new TypeError('"index" must be an Integer!');
65
+ throw err;
66
+ }
67
+
68
+ this.middlewares.splice(middlewareIndex, 1);
69
+ debug(`removed middleware (${ index })`);
70
+ return this;
71
+ }
72
+
73
+
74
+ /*
75
+ * Prepare stack for processing.
76
+ *
77
+ * @api public
78
+ */
79
+ lock() {
80
+ // Add final handler to the stack.
81
+ this.add((req, res)=>this.finalhandler(req, res)());
82
+
83
+ // Stack is ready.
84
+ this.isLocked = true;
85
+
86
+ debug(`stack is locked`);
87
+ }
88
+
89
+
90
+ /*
91
+ * Unlocks stack.
92
+ *
93
+ * @api public
94
+ */
95
+ unlock() {
96
+ this.isLocked = false;
97
+ this.middlewares.pop();
98
+
99
+ debug(`stack is unlocked`);
100
+ }
101
+
102
+
103
+ /*
104
+ * Start chain.
105
+ *
106
+ * @api public
107
+ */
108
+ process(req, res) {
109
+ let middlewareOffset = -1;
110
+
111
+ const next = (...args) => {
112
+ middlewareOffset += 1;
113
+ const fn = this.middlewares[middlewareOffset];
114
+
115
+ try {
116
+ return fn.call(null, req, res, next, ...args);
117
+ }
118
+ catch(error) {
119
+ return this.finalhandler(req, res)(error);
120
+ }
121
+ }
122
+
123
+ return next();
124
+ }
125
+ }