nodester 0.6.5 → 0.6.7
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/Readme.md +12 -4
- package/lib/application/index.js +1 -1
- package/lib/constants/Bounds.js +7 -0
- package/lib/constants/Clauses.js +7 -0
- package/lib/constants/ErrorCodes.js +7 -0
- package/lib/constants/FileExtensions.js +44 -0
- package/lib/constants/Operators.js +30 -0
- package/lib/constants/ResponseFormats.js +7 -0
- package/lib/http/codes/descriptions.js +1 -0
- package/lib/http/methods/index.js +29 -0
- package/lib/middlewares/404/index.js +38 -0
- package/lib/middlewares/etag/index.js +81 -0
- package/lib/middlewares/render/index.js +62 -0
- package/lib/router/index.js +4 -2
- package/lib/router/routes.util.js +21 -1
- package/lib/router/utils.js +1 -1
- package/package.json +5 -4
- package/tests/ast.js +18 -0
- package/tests/index.test.js +2 -3
package/Readme.md
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/MarkKhramko/nodester/refs/heads/main/.github/assets/nodester_logo.png">
|
|
4
|
+
<img alt="nodester logo" src="https://raw.githubusercontent.com/MarkKhramko/nodester/refs/heads/main/.github/assets/nodester_logo.png">
|
|
5
|
+
</picture>
|
|
6
|
+
</div>
|
|
2
7
|
|
|
3
|
-
[](https://www.npmjs.com/package/nodester)
|
|
4
|
-
[](https://www.npmjs.com/package/nodester)
|
|
8
|
+
# nodester [](https://www.npmjs.com/package/nodester) [](https://www.npmjs.com/package/nodester)
|
|
5
9
|
|
|
6
10
|
> **nodester** is a Node.js framework designed to solve the problem of a complex data querying over HTTP.
|
|
7
11
|
|
|
@@ -39,10 +43,14 @@ const db = require('#db');
|
|
|
39
43
|
const app = new nodester();
|
|
40
44
|
app.set.database(db);
|
|
41
45
|
|
|
46
|
+
// Do any synchronous initializations
|
|
47
|
+
// before app.listen here
|
|
48
|
+
// ...
|
|
49
|
+
|
|
42
50
|
// Optional beforeStart hook:
|
|
43
51
|
app.beforeStart(async () => {
|
|
44
52
|
// Do any asynchronous initializations
|
|
45
|
-
// before app.listen
|
|
53
|
+
// before app.listen here
|
|
46
54
|
// ...
|
|
47
55
|
});
|
|
48
56
|
|
package/lib/application/index.js
CHANGED
package/lib/constants/Bounds.js
CHANGED
package/lib/constants/Clauses.js
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
// JS:
|
|
9
|
+
const jsCodeExtensions = [
|
|
10
|
+
'js', // built-in
|
|
11
|
+
'cjs', // https://nodejs.org/api/esm.html#esm_enabling
|
|
12
|
+
'mjs', // https://nodejs.org/api/esm.html#esm_enabling
|
|
13
|
+
'iced', // http://maxtaco.github.io/coffee-script/
|
|
14
|
+
'liticed', // http://maxtaco.github.io/coffee-script/ (literate iced)
|
|
15
|
+
'iced.md', // http://maxtaco.github.io/coffee-script/ (literate iced)
|
|
16
|
+
'coffee', // http://coffeescript.org/
|
|
17
|
+
'litcoffee', // http://coffeescript.org/ (literate coffee)
|
|
18
|
+
'coffee.md', // http://coffeescript.org/ (literate coffee)
|
|
19
|
+
'ts', // https://www.typescriptlang.org/
|
|
20
|
+
'tsx', // https://www.typescriptlang.org/docs/handbook/jsx.html
|
|
21
|
+
'cs', // http://bridge.net/ ??
|
|
22
|
+
'ls', // http://livescript.net/
|
|
23
|
+
'es6', // https://babeljs.io
|
|
24
|
+
'es', // https://babeljs.io
|
|
25
|
+
'jsx', // https://babeljs.io https://facebook.github.io/jsx/.
|
|
26
|
+
'sjs', // http://onilabs.com/stratifiedjs
|
|
27
|
+
'co', // http://satyr.github.io/coco/
|
|
28
|
+
'eg' // http://www.earl-grey.io/
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const jsConfigExtensions = [
|
|
32
|
+
'json', // built-in
|
|
33
|
+
'json.ls', // http://livescript.net/
|
|
34
|
+
'json5' // http://json5.org/
|
|
35
|
+
];
|
|
36
|
+
// JS\
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
js: {
|
|
41
|
+
code: jsCodeExtensions,
|
|
42
|
+
config: jsConfigExtensions
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const Enum = require('nodester/enum');
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
module.exports = new Enum({
|
|
12
|
+
and: Symbol.for('and'),
|
|
13
|
+
between: Symbol.for('between'),
|
|
14
|
+
contains: Symbol.for('contains'),
|
|
15
|
+
eq: Symbol.for('eq'),
|
|
16
|
+
ne: Symbol.for('ne'),
|
|
17
|
+
gte: Symbol.for('gte'),
|
|
18
|
+
gt: Symbol.for('gt'),
|
|
19
|
+
lte: Symbol.for('lte'),
|
|
20
|
+
lt: Symbol.for('lt'),
|
|
21
|
+
not: Symbol.for('not'),
|
|
22
|
+
is: Symbol.for('is'),
|
|
23
|
+
in: Symbol.for('in'),
|
|
24
|
+
notIn: Symbol.for('notIn'),
|
|
25
|
+
like: Symbol.for('like'),
|
|
26
|
+
notLike: Symbol.for('notLike'),
|
|
27
|
+
notBetween: Symbol.for('notBetween'),
|
|
28
|
+
or: Symbol.for('or'),
|
|
29
|
+
xor: Symbol.for('xor'),
|
|
30
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const Enum = require('nodester/enum');
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
module.exports = new Enum({
|
|
12
|
+
// Core REST methods:
|
|
13
|
+
GET: 'GET',
|
|
14
|
+
POST: 'POST',
|
|
15
|
+
PUT: 'PUT',
|
|
16
|
+
PATCH: 'PATCH',
|
|
17
|
+
DELETE: 'DELETE',
|
|
18
|
+
// Additional but common in REST APIs:
|
|
19
|
+
HEAD: 'HEAD',
|
|
20
|
+
OPTIONS: 'OPTIONS',
|
|
21
|
+
|
|
22
|
+
// Search and non-standard querying:
|
|
23
|
+
SEARCH: 'SEARCH',
|
|
24
|
+
QUERY: 'QUERY',
|
|
25
|
+
|
|
26
|
+
// Rarely used in REST but valid HTTP:
|
|
27
|
+
TRACE: 'TRACE',
|
|
28
|
+
CONNECT: 'CONNECT',
|
|
29
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
HTTP_CODE_NOT_FOUND
|
|
10
|
+
} = require('nodester/http/codes');
|
|
11
|
+
|
|
12
|
+
const { createErrorResponse } = require('nodester/factories/responses/rest');
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Initialize `404` middleware.
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} [options]
|
|
19
|
+
*
|
|
20
|
+
* @return {Function}
|
|
21
|
+
*
|
|
22
|
+
* @access public
|
|
23
|
+
*/
|
|
24
|
+
module.exports = function init404Middleware(options={}) {
|
|
25
|
+
const context = {
|
|
26
|
+
options
|
|
27
|
+
}
|
|
28
|
+
return handle.bind(context);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function handle(req, res, next) {
|
|
32
|
+
const err = new Error('Route not found');
|
|
33
|
+
|
|
34
|
+
return createErrorResponse(res, {
|
|
35
|
+
error: err,
|
|
36
|
+
status: HTTP_CODE_NOT_FOUND
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const calculate = require('etag');
|
|
9
|
+
const Stream = require('stream');
|
|
10
|
+
|
|
11
|
+
// Utils:
|
|
12
|
+
const promisify = require('util').promisify;
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const getFileStats = promisify(fs.stat);
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
module.exports = initETagMiddleware;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Initialize `etag` middleware.
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} [options]
|
|
23
|
+
* @param {boolean} options.weak
|
|
24
|
+
*
|
|
25
|
+
* @return {Function}
|
|
26
|
+
*
|
|
27
|
+
* @access public
|
|
28
|
+
*/
|
|
29
|
+
function initETagMiddleware(options) {
|
|
30
|
+
const context = {
|
|
31
|
+
options
|
|
32
|
+
}
|
|
33
|
+
return handle.bind(context);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Add ETag header field.
|
|
38
|
+
*/
|
|
39
|
+
function handle(req, res, next) {
|
|
40
|
+
// console.log('e', req.headers);
|
|
41
|
+
return next();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function _getResponseEntity(res) {
|
|
45
|
+
// If body is not defined:
|
|
46
|
+
const { body } = res;
|
|
47
|
+
if (!body || res.get('etag'))
|
|
48
|
+
return;
|
|
49
|
+
|
|
50
|
+
// Status code.
|
|
51
|
+
const status = res.statusCode / 100 | 0;
|
|
52
|
+
|
|
53
|
+
console.log('getResponseEntity', status, { tag: res.get('etag') });
|
|
54
|
+
|
|
55
|
+
// 2xx
|
|
56
|
+
if (status !== 2)
|
|
57
|
+
return;
|
|
58
|
+
|
|
59
|
+
if (body instanceof Stream) {
|
|
60
|
+
if (!body.path)
|
|
61
|
+
return;
|
|
62
|
+
|
|
63
|
+
const stats = await getFileStats(body.path);
|
|
64
|
+
return stats;
|
|
65
|
+
}
|
|
66
|
+
else if (typeof body === 'string' || Buffer.isBuffer(body)) {
|
|
67
|
+
return body;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return JSON.stringify(body);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function _setEtag(res, entity, options) {
|
|
75
|
+
console.log('setEtag', typeof res, entity);
|
|
76
|
+
|
|
77
|
+
if (!entity)
|
|
78
|
+
return;
|
|
79
|
+
|
|
80
|
+
res.etag = calculate(entity, options);
|
|
81
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
module.exports = function initRenderMiddleware() {
|
|
10
|
+
return handle;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
function handle(req, res, next) {
|
|
15
|
+
const context = { req, res, next };
|
|
16
|
+
res.render = _render.bind(context);
|
|
17
|
+
next();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Render `view` with the given `options` and optional callback `fn`.
|
|
23
|
+
* When a callback function is given a response will _not_ be made
|
|
24
|
+
* automatically, otherwise a response of _200_ and _text/html_ is given.
|
|
25
|
+
*
|
|
26
|
+
* Options:
|
|
27
|
+
*
|
|
28
|
+
* - `cache` boolean hinting to the engine it should cache
|
|
29
|
+
* - `filename` filename of the view being rendered
|
|
30
|
+
*
|
|
31
|
+
* @alias render
|
|
32
|
+
* @access public
|
|
33
|
+
*/
|
|
34
|
+
function _render(view, options, callback) {
|
|
35
|
+
const app = this.req.app;
|
|
36
|
+
let done = callback;
|
|
37
|
+
let opts = options || {};
|
|
38
|
+
|
|
39
|
+
const req = this.req;
|
|
40
|
+
const res = this.res;
|
|
41
|
+
const next = this.next;
|
|
42
|
+
|
|
43
|
+
// support callback function as second arg
|
|
44
|
+
if (typeof options === 'function') {
|
|
45
|
+
done = options;
|
|
46
|
+
opts = {};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// merge res.locals
|
|
50
|
+
opts._locals = res.locals;
|
|
51
|
+
|
|
52
|
+
// default callback to respond
|
|
53
|
+
done = done || function (err, str) {
|
|
54
|
+
if (err)
|
|
55
|
+
return next(err);
|
|
56
|
+
|
|
57
|
+
res.send(str);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// render
|
|
61
|
+
app.render(view, opts, done);
|
|
62
|
+
};
|
package/lib/router/index.js
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
// Constants.
|
|
9
|
+
const FileExtensions = require('nodester/constants/FileExtensions');
|
|
10
|
+
|
|
8
11
|
const MiddlewaresStack = require('nodester/stacks/middlewares');
|
|
9
12
|
const Route = require('./route');
|
|
10
13
|
|
|
@@ -22,7 +25,6 @@ const { parseProviderFileNames } = require('./utils');
|
|
|
22
25
|
// File system:
|
|
23
26
|
const Path = require('path');
|
|
24
27
|
const fs = require('fs');
|
|
25
|
-
const commonExtensions = require('common-js-file-extensions');
|
|
26
28
|
|
|
27
29
|
// Arguments validator.
|
|
28
30
|
const { ensure } = require('nodester/validators/arguments');
|
|
@@ -59,7 +61,7 @@ module.exports = class NodesterRouter {
|
|
|
59
61
|
// Reference to the providers stack.
|
|
60
62
|
this._providers = new Map();
|
|
61
63
|
|
|
62
|
-
this.codeFileExtensions =
|
|
64
|
+
this.codeFileExtensions = FileExtensions.js.code;
|
|
63
65
|
this.paths = {};
|
|
64
66
|
|
|
65
67
|
// Options:
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
const HTTP_METHODS = require('nodester/http/methods');
|
|
9
|
+
|
|
8
10
|
const MiddlewaresStack = require('nodester/stacks/middlewares');
|
|
9
11
|
|
|
10
12
|
// Utils:
|
|
@@ -20,7 +22,7 @@ module.exports = {
|
|
|
20
22
|
|
|
21
23
|
function _validateParsedRouteMethood(parsedRoute) {
|
|
22
24
|
if (!parsedRoute || parsedRoute?.method === undefined) {
|
|
23
|
-
const err = new TypeError(`
|
|
25
|
+
const err = new TypeError(`Route definition must start with one of the following methods: ${ HTTP_METHODS.asArray.join(', ') } or any other custom one.`);
|
|
24
26
|
throw err;
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -52,7 +54,16 @@ function _wrapRouteHandler(routeInstance, handler) {
|
|
|
52
54
|
if (parsedHandler.controllerName !== undefined) {
|
|
53
55
|
// Get method (action) from Controller:
|
|
54
56
|
const controller = this._controllers.get(parsedHandler.controllerName);
|
|
57
|
+
if (!controller) {
|
|
58
|
+
const err = new TypeError(`Controller named "${ parsedHandler.controllerName }" is not defined.`);
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
|
|
55
62
|
const controllerAction = controller[parsedHandler.actionName];
|
|
63
|
+
if (!controllerAction) {
|
|
64
|
+
const err = new TypeError(`Handler "${ parsedHandler.actionName }" is not defined in controller "${ parsedHandler.controllerName }".`);
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
56
67
|
|
|
57
68
|
providedAction = controllerAction;
|
|
58
69
|
}
|
|
@@ -62,7 +73,16 @@ function _wrapRouteHandler(routeInstance, handler) {
|
|
|
62
73
|
else {
|
|
63
74
|
// Get method (action) from Provider:
|
|
64
75
|
const provider = this._providers.get(parsedHandler.providerName);
|
|
76
|
+
if (!provider) {
|
|
77
|
+
const err = new TypeError(`Provider named "${ parsedHandler.providerName }" is not defined.`);
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
|
|
65
81
|
const providerAction = provider[parsedHandler.actionName];
|
|
82
|
+
if (!providerAction) {
|
|
83
|
+
const err = new TypeError(`Handler "${ parsedHandler.actionName }" is not defined in provider "${ parsedHandler.providerName }".`);
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
66
86
|
|
|
67
87
|
providedAction = providerAction;
|
|
68
88
|
}
|
package/lib/router/utils.js
CHANGED
|
@@ -10,7 +10,7 @@ module.exports = {
|
|
|
10
10
|
parseProviderFileNames: _parseProviderFileNames
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
function _parseProviderFileNames(fileNames, availableFileExtensions, term='controller') {
|
|
13
|
+
function _parseProviderFileNames(fileNames=[], availableFileExtensions=[], term='controller') {
|
|
14
14
|
const resultNames = [];
|
|
15
15
|
|
|
16
16
|
for (const fileName of fileNames) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodester",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "A versatile REST framework for Node.js",
|
|
5
5
|
"directories": {
|
|
6
6
|
"docs": "docs",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"./body/extract": "./lib/body/extract.js",
|
|
17
17
|
|
|
18
18
|
"./constants/ErrorCodes": "./lib/constants/ErrorCodes.js",
|
|
19
|
+
"./constants/FileExtensions": "./lib/constants/FileExtensions.js",
|
|
19
20
|
|
|
20
21
|
"./controllers/methods": "./lib/controllers/methods/index.js",
|
|
21
22
|
"./controllers/mixins": "./lib/controllers/mixins/index.js",
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
"./http/codes": "./lib/http/codes/index.js",
|
|
43
44
|
"./http/codes/descriptions": "./lib/http/codes/descriptions.js",
|
|
44
45
|
"./http/codes/symbols": "./lib/http/codes/symbols.js",
|
|
46
|
+
"./http/methods": "./lib/http/methods/index.js",
|
|
45
47
|
|
|
46
48
|
"./loggers/console": "./lib/loggers/console.js",
|
|
47
49
|
"./loggers/dev": "./lib/loggers/dev.js",
|
|
@@ -78,7 +80,6 @@
|
|
|
78
80
|
"dependencies": {
|
|
79
81
|
"@js-temporal/polyfill": "^0.4.3",
|
|
80
82
|
"body-parser": "^1.20.2",
|
|
81
|
-
"common-js-file-extensions": "^1.0.4",
|
|
82
83
|
"cookie": "^1.0.0",
|
|
83
84
|
"cookie-signature": "^1.2.0",
|
|
84
85
|
"debug": "^4.3.4",
|
|
@@ -89,8 +90,8 @@
|
|
|
89
90
|
"http-errors": "^2.0.0",
|
|
90
91
|
"inflection": "^2.0.1",
|
|
91
92
|
"mime": "^3.0.0",
|
|
92
|
-
"mysql2": "^3.
|
|
93
|
-
"pg": "^8.
|
|
93
|
+
"mysql2": "^3.14.4",
|
|
94
|
+
"pg": "^8.16.3",
|
|
94
95
|
"pg-hstore": "^2.3.4",
|
|
95
96
|
"qs": "^6.11.0",
|
|
96
97
|
"sequelize": "^6.6.5"
|
package/tests/ast.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { ModelsTree } = require('../lib/middlewares/ql/sequelize/interpreter/ModelsTree');
|
|
2
|
+
const { AST_ModelsTree } = require('../lib/tools/nql.tool');
|
|
3
|
+
|
|
4
|
+
const tree = new ModelsTree();
|
|
5
|
+
tree.node.addWhere({ id: ['1000'] });
|
|
6
|
+
tree.include('comments').use('comments');
|
|
7
|
+
tree.node.order = 'desc';
|
|
8
|
+
tree.up();
|
|
9
|
+
tree.include('users');
|
|
10
|
+
tree.include('likes') && tree.use('likes');
|
|
11
|
+
tree.node.order = 'rand';
|
|
12
|
+
tree.up();
|
|
13
|
+
tree.include('reposts');
|
|
14
|
+
|
|
15
|
+
console.debug(
|
|
16
|
+
AST_ModelsTree(tree)
|
|
17
|
+
);
|
|
18
|
+
|
package/tests/index.test.js
CHANGED
|
@@ -9,14 +9,13 @@ const {
|
|
|
9
9
|
// Configs.
|
|
10
10
|
const PORT = 8100;
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
const Nodester = require('../lib/application');
|
|
12
|
+
const nodester = require('nodester');
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
describe('nodester application', () => {
|
|
17
16
|
|
|
18
17
|
// Init.
|
|
19
|
-
const app = new
|
|
18
|
+
const app = new nodester();
|
|
20
19
|
it('construct', () => {
|
|
21
20
|
expect(app).toBeDefined();
|
|
22
21
|
});
|