@adaptivestone/framework 5.0.0-beta.4 → 5.0.0-beta.6
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/CHANGELOG.md +10 -1
- package/Cli.js +2 -3
- package/cluster.js +0 -2
- package/commands/CreateUser.js +31 -0
- package/commands/DropIndex.js +14 -5
- package/commands/GetOpenApiJson.js +13 -2
- package/commands/SyncIndexes.js +1 -0
- package/commands/migration/Create.js +21 -10
- package/commands/migration/Migrate.js +1 -0
- package/controllers/Auth.js +19 -25
- package/eslint.config.js +68 -0
- package/helpers/files.js +1 -4
- package/helpers/logger.js +0 -1
- package/helpers/yup.js +2 -4
- package/jsconfig.json +3 -3
- package/models/User.js +0 -2
- package/modules/AbstractCommand.js +10 -1
- package/modules/AbstractController.js +0 -7
- package/modules/Base.js +3 -2
- package/modules/BaseCli.js +66 -6
- package/package.json +9 -7
- package/server.d.ts +3 -2
- package/server.js +3 -4
- package/services/cache/Cache.js +1 -1
- package/services/http/HttpServer.js +6 -7
- package/services/http/middleware/AbstractMiddleware.js +3 -3
- package/services/http/middleware/I18n.js +1 -1
- package/services/http/middleware/Pagination.js +4 -4
- package/services/messaging/email/index.js +2 -2
- package/services/validate/ValidateService.js +4 -4
- package/services/validate/drivers/AbstractValidator.js +2 -2
- package/services/validate/drivers/CustomValidator.js +2 -2
- package/services/validate/drivers/YupValidator.js +3 -3
- package/tests/setup.js +6 -3
- package/tests/setupVitest.js +1 -3
- package/types/ICommandArguments.d.ts +41 -0
- package/vitest.config.js +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
### 5.0.0-beta.5
|
|
2
|
+
|
|
3
|
+
[BREAKING] remove minimist CLI parsing and replace it by commandArguments parser
|
|
4
|
+
[UPDATE] migrated from eslint-plugin-import to eslint-plugin-import-x
|
|
5
|
+
|
|
6
|
+
### 5.0.0-beta.5
|
|
7
|
+
|
|
8
|
+
[UPDATE] migrate to eslint 9 and away from aibnb styles (they are abonded)
|
|
9
|
+
|
|
1
10
|
### 5.0.0-beta.4
|
|
2
11
|
|
|
3
12
|
[NEW] on shutdown event now after timeout we are forcing to shutdown
|
|
@@ -462,7 +471,7 @@ await doc.populate([
|
|
|
462
471
|
[UPDATE] update deps
|
|
463
472
|
[UPDATE] winston console transport now using timestapms
|
|
464
473
|
[UPDATE] PrepareAppInfo middleware now a global one. Do not need to include it on every controller
|
|
465
|
-
[NEW] Request
|
|
474
|
+
[NEW] Request also works with req.query, but req.body have bigger priority
|
|
466
475
|
|
|
467
476
|
### 2.18.0
|
|
468
477
|
|
package/Cli.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import parseArgs from 'minimist';
|
|
2
1
|
import mongoose from 'mongoose';
|
|
3
2
|
import BaseCli from './modules/BaseCli.js';
|
|
4
3
|
import Server from './server.js';
|
|
@@ -8,14 +7,14 @@ class Cli extends BaseCli {
|
|
|
8
7
|
mongoose.set('autoIndex', false); // we do not need create indexes on CLI.
|
|
9
8
|
const server = new Server(serverConfig);
|
|
10
9
|
super(server);
|
|
11
|
-
this.args = parseArgs(process.argv.slice(3));
|
|
12
10
|
}
|
|
13
11
|
|
|
14
12
|
async run() {
|
|
15
13
|
await this.server.init({ isSkipModelInit: true, isSkipModelLoading: true });
|
|
16
14
|
const command = process.argv[2]?.toLowerCase();
|
|
17
|
-
await super.run(command
|
|
15
|
+
await super.run(command);
|
|
18
16
|
this.app.events.emit('shutdown');
|
|
17
|
+
return true;
|
|
19
18
|
}
|
|
20
19
|
}
|
|
21
20
|
|
package/cluster.js
CHANGED
|
@@ -4,7 +4,6 @@ import { cpus } from 'node:os';
|
|
|
4
4
|
const numCPUs = cpus().length;
|
|
5
5
|
|
|
6
6
|
if (cluster.isPrimary) {
|
|
7
|
-
// eslint-disable-next-line no-console
|
|
8
7
|
console.log(`Master ${process.pid} is running`);
|
|
9
8
|
// Fork workers.
|
|
10
9
|
for (let i = 0; i < numCPUs; i += 1) {
|
|
@@ -12,7 +11,6 @@ if (cluster.isPrimary) {
|
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
cluster.on('exit', (worker, code, signal) => {
|
|
15
|
-
// eslint-disable-next-line no-console
|
|
16
14
|
console.log(
|
|
17
15
|
`Worker \x1B[45m ${
|
|
18
16
|
worker.process.pid
|
package/commands/CreateUser.js
CHANGED
|
@@ -6,6 +6,37 @@ class CreateUser extends AbstractCommand {
|
|
|
6
6
|
return 'Create user in a database';
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* You able to add command arguments for parsing there.
|
|
11
|
+
* @returns {import("../types/ICommandArguments.js").ICommandArguments}
|
|
12
|
+
*/
|
|
13
|
+
static get commandArguments() {
|
|
14
|
+
return {
|
|
15
|
+
id: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'User id to find user',
|
|
18
|
+
},
|
|
19
|
+
email: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'User id to find/create user',
|
|
22
|
+
},
|
|
23
|
+
password: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'New password for user',
|
|
26
|
+
},
|
|
27
|
+
roles: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description:
|
|
30
|
+
'User roles comma separated string (--roles=user,admin,someOtherRoles)',
|
|
31
|
+
},
|
|
32
|
+
update: {
|
|
33
|
+
type: 'boolean',
|
|
34
|
+
default: false,
|
|
35
|
+
description: 'Update user if it exists',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
9
40
|
async run() {
|
|
10
41
|
const User = this.app.getModel('User');
|
|
11
42
|
const { id, email, password, roles, update } = this.args;
|
package/commands/DropIndex.js
CHANGED
|
@@ -5,12 +5,21 @@ class DropIndex extends AbstractCommand {
|
|
|
5
5
|
return 'Drop indexes of model';
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
/**
|
|
9
|
+
* You able to add command arguments for parsing there.
|
|
10
|
+
* @returns {import("../types/ICommandArguments.js").ICommandArguments}
|
|
11
|
+
*/
|
|
12
|
+
static get commandArguments() {
|
|
13
|
+
return {
|
|
14
|
+
model: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
description: 'Model name',
|
|
17
|
+
required: true,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
13
21
|
|
|
22
|
+
async run() {
|
|
14
23
|
const Model = this.app.getModel(this.args.model);
|
|
15
24
|
|
|
16
25
|
if (!Model) {
|
|
@@ -8,6 +8,18 @@ class GetOpenApiJson extends AbstractCommand {
|
|
|
8
8
|
static get description() {
|
|
9
9
|
return 'Generate documentation (openApi) ';
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* You able to add command arguments for parsing there.
|
|
13
|
+
* @returns {import("../types/ICommandArguments.js").ICommandArguments}
|
|
14
|
+
*/
|
|
15
|
+
static get commandArguments() {
|
|
16
|
+
return {
|
|
17
|
+
output: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'Output file path',
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
11
23
|
|
|
12
24
|
async run() {
|
|
13
25
|
const { myDomain } = this.app.getConfig('http');
|
|
@@ -18,7 +30,7 @@ class GetOpenApiJson extends AbstractCommand {
|
|
|
18
30
|
|
|
19
31
|
try {
|
|
20
32
|
jsonFile = JSON.parse(await fs.readFile(jsonFile, 'utf8'));
|
|
21
|
-
} catch
|
|
33
|
+
} catch {
|
|
22
34
|
this.logger.error(
|
|
23
35
|
'No npm package detected. Please start this command via NPM as it depends on package.json',
|
|
24
36
|
);
|
|
@@ -123,7 +135,6 @@ class GetOpenApiJson extends AbstractCommand {
|
|
|
123
135
|
|
|
124
136
|
let routeName = Object.keys(route)[0];
|
|
125
137
|
if (routeName === '/') {
|
|
126
|
-
// eslint-disable-next-line no-continue
|
|
127
138
|
continue;
|
|
128
139
|
}
|
|
129
140
|
|
package/commands/SyncIndexes.js
CHANGED
|
@@ -7,28 +7,39 @@ class CreateMigration extends AbstractCommand {
|
|
|
7
7
|
return 'Create new migration';
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* You able to add command arguments for parsing there.
|
|
12
|
+
* @returns {import("../../types/ICommandArguments.js").ICommandArguments}
|
|
13
|
+
*/
|
|
14
|
+
static get commandArguments() {
|
|
15
|
+
return {
|
|
16
|
+
name: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Migration name',
|
|
19
|
+
required: true,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
10
24
|
async run() {
|
|
11
|
-
if (!this.args.name) {
|
|
12
|
-
return this.logger.error(
|
|
13
|
-
'Please provide migration name with key "--name={someName}"',
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
25
|
if (this.args.name.match(/^\d/)) {
|
|
17
|
-
|
|
26
|
+
this.logger.error('Command cant start from nubmer');
|
|
27
|
+
return false;
|
|
18
28
|
}
|
|
19
|
-
const fileName = `${Date.now()}_${
|
|
29
|
+
const fileName = `${Date.now()}_${CreateMigration.camelSentence(
|
|
20
30
|
this.args.name,
|
|
21
31
|
)}.js`;
|
|
22
32
|
|
|
23
|
-
const fileContent =
|
|
24
|
-
|
|
33
|
+
const fileContent = CreateMigration.getTemplate(
|
|
34
|
+
CreateMigration.camelSentence(this.args.name),
|
|
25
35
|
);
|
|
26
36
|
|
|
27
37
|
await fs.writeFile(
|
|
28
38
|
path.join(this.app.foldersConfig.migrations, fileName),
|
|
29
39
|
fileContent,
|
|
30
40
|
);
|
|
31
|
-
|
|
41
|
+
this.logger.info(`Migration created ${fileName}`);
|
|
42
|
+
return true;
|
|
32
43
|
}
|
|
33
44
|
|
|
34
45
|
static camelSentence(str) {
|
package/controllers/Auth.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { object, string } from 'yup';
|
|
2
2
|
import AbstractController from '../modules/AbstractController.js';
|
|
3
3
|
import GetUserByToken from '../services/http/middleware/GetUserByToken.js';
|
|
4
4
|
import RateLimiter from '../services/http/middleware/RateLimiter.js';
|
|
@@ -9,58 +9,52 @@ class Auth extends AbstractController {
|
|
|
9
9
|
post: {
|
|
10
10
|
'/login': {
|
|
11
11
|
handler: this.postLogin,
|
|
12
|
-
request:
|
|
13
|
-
email:
|
|
14
|
-
password:
|
|
12
|
+
request: object().shape({
|
|
13
|
+
email: string().email().required('auth.emailProvided'), // if not provided then error will be generated
|
|
14
|
+
password: string().required('auth.passwordProvided'), // possible to provide values from translation
|
|
15
15
|
}),
|
|
16
16
|
},
|
|
17
17
|
'/register': {
|
|
18
18
|
handler: this.postRegister,
|
|
19
|
-
request:
|
|
20
|
-
email:
|
|
21
|
-
.string()
|
|
19
|
+
request: object().shape({
|
|
20
|
+
email: string()
|
|
22
21
|
.email('auth.emailValid')
|
|
23
22
|
.required('auth.emailProvided'),
|
|
24
|
-
password:
|
|
25
|
-
.string()
|
|
23
|
+
password: string()
|
|
26
24
|
.matches(
|
|
27
25
|
/^[a-zA-Z0-9!@#$%ˆ^&*()_+\-{}[\]<>]+$/,
|
|
28
26
|
'auth.passwordValid',
|
|
29
27
|
)
|
|
30
28
|
.required('auth.passwordProvided'),
|
|
31
|
-
nickName:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
nickName: string().matches(
|
|
30
|
+
/^[a-zA-Z0-9_\-.]+$/,
|
|
31
|
+
'auth.nickNameValid',
|
|
32
|
+
),
|
|
33
|
+
firstName: string(),
|
|
34
|
+
lastName: string(),
|
|
36
35
|
}),
|
|
37
36
|
},
|
|
38
37
|
'/logout': this.postLogout,
|
|
39
38
|
'/verify': this.verifyUser,
|
|
40
39
|
'/send-recovery-email': {
|
|
41
40
|
handler: this.sendPasswordRecoveryEmail,
|
|
42
|
-
request:
|
|
43
|
-
.object()
|
|
44
|
-
.shape({ email: yup.string().email().required() }),
|
|
41
|
+
request: object().shape({ email: string().email().required() }),
|
|
45
42
|
},
|
|
46
43
|
'/recover-password': {
|
|
47
44
|
handler: this.recoverPassword,
|
|
48
|
-
request:
|
|
49
|
-
password:
|
|
50
|
-
.string()
|
|
45
|
+
request: object().shape({
|
|
46
|
+
password: string()
|
|
51
47
|
.matches(
|
|
52
48
|
/^[a-zA-Z0-9!@#$%ˆ^&*()_+\-{}[\]<>]+$/,
|
|
53
49
|
'auth.passwordValid',
|
|
54
50
|
)
|
|
55
51
|
.required(),
|
|
56
|
-
passwordRecoveryToken:
|
|
52
|
+
passwordRecoveryToken: string().required(),
|
|
57
53
|
}),
|
|
58
54
|
},
|
|
59
55
|
'/send-verification': {
|
|
60
56
|
handler: this.sendVerification,
|
|
61
|
-
request:
|
|
62
|
-
.object()
|
|
63
|
-
.shape({ email: yup.string().email().required() }),
|
|
57
|
+
request: object().shape({ email: string().email().required() }),
|
|
64
58
|
},
|
|
65
59
|
},
|
|
66
60
|
};
|
|
@@ -136,7 +130,7 @@ class Auth extends AbstractController {
|
|
|
136
130
|
user = await User.getUserByVerificationToken(
|
|
137
131
|
req.query.verification_token,
|
|
138
132
|
);
|
|
139
|
-
} catch
|
|
133
|
+
} catch {
|
|
140
134
|
return res.status(400).json({
|
|
141
135
|
message: req.i18n.t('email.alreadyVerifiedOrWrongToken'),
|
|
142
136
|
});
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import globals from 'globals';
|
|
2
|
+
import pluginJs from '@eslint/js';
|
|
3
|
+
import importPlugin from 'eslint-plugin-import-x';
|
|
4
|
+
import vitest from '@vitest/eslint-plugin';
|
|
5
|
+
import eslintConfigPrettier from 'eslint-config-prettier';
|
|
6
|
+
import prettierPlugin from 'eslint-plugin-prettier/recommended';
|
|
7
|
+
|
|
8
|
+
/** @type {import('eslint').Linter.Config[]} */
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
export default [
|
|
11
|
+
pluginJs.configs.recommended,
|
|
12
|
+
importPlugin.flatConfigs.recommended,
|
|
13
|
+
eslintConfigPrettier,
|
|
14
|
+
prettierPlugin,
|
|
15
|
+
{
|
|
16
|
+
languageOptions: {
|
|
17
|
+
sourceType: 'module',
|
|
18
|
+
ecmaVersion: 'latest',
|
|
19
|
+
globals: {
|
|
20
|
+
...globals.es2023,
|
|
21
|
+
...globals.node,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
rules: {
|
|
27
|
+
'no-await-in-loop': 'error',
|
|
28
|
+
'no-param-reassign': 'error',
|
|
29
|
+
'class-methods-use-this': 'error',
|
|
30
|
+
'no-shadow': 'error',
|
|
31
|
+
'prefer-const': 'error',
|
|
32
|
+
'import-x/no-extraneous-dependencies': ['error'],
|
|
33
|
+
'import-x/first': ['error'],
|
|
34
|
+
camelcase: ['error', { properties: 'never', ignoreDestructuring: false }],
|
|
35
|
+
'prefer-destructuring': [
|
|
36
|
+
'error',
|
|
37
|
+
{
|
|
38
|
+
VariableDeclarator: {
|
|
39
|
+
array: false,
|
|
40
|
+
object: true,
|
|
41
|
+
},
|
|
42
|
+
AssignmentExpression: {
|
|
43
|
+
array: true,
|
|
44
|
+
object: false,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
enforceForRenamedProperties: false,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
'no-plusplus': 'error',
|
|
52
|
+
'consistent-return': 'error',
|
|
53
|
+
'no-return-await': 'error',
|
|
54
|
+
'arrow-body-style': 'error',
|
|
55
|
+
'dot-notation': 'error',
|
|
56
|
+
curly: 'error',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
files: ['**/*.test.js'],
|
|
61
|
+
plugins: {
|
|
62
|
+
vitest,
|
|
63
|
+
},
|
|
64
|
+
rules: {
|
|
65
|
+
...vitest.configs.recommended.rules,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
];
|
package/helpers/files.js
CHANGED
package/helpers/logger.js
CHANGED
package/helpers/yup.js
CHANGED
|
@@ -13,12 +13,10 @@ class YupFile extends Schema {
|
|
|
13
13
|
constructor() {
|
|
14
14
|
super({
|
|
15
15
|
type: 'file',
|
|
16
|
+
// @ts-ignore
|
|
16
17
|
check: (value) => value.every((item) => item instanceof PersistentFile),
|
|
17
18
|
});
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export {
|
|
22
|
-
// eslint-disable-next-line import/prefer-default-export
|
|
23
|
-
YupFile,
|
|
24
|
-
};
|
|
22
|
+
export { YupFile };
|
package/jsconfig.json
CHANGED
package/models/User.js
CHANGED
|
@@ -26,9 +26,18 @@ class AbstractCommand extends Base {
|
|
|
26
26
|
return `CLI: ${commandName} ${JSON.stringify(args)}`;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* You able to add command arguments for parsing there.
|
|
31
|
+
* @see https://nodejs.org/api/util.html#utilparseargsconfig in config.options plus extended with description and required
|
|
32
|
+
* @returns {import("../types/ICommandArguments.js").ICommandArguments}
|
|
33
|
+
*/
|
|
34
|
+
static get commandArguments() {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
|
|
29
38
|
/**
|
|
30
39
|
* Entry point to every command. This method should be overridden
|
|
31
|
-
* @return {Promise<boolean>}
|
|
40
|
+
* @return {Promise<boolean>} result
|
|
32
41
|
*/
|
|
33
42
|
async run() {
|
|
34
43
|
this.logger.error('You should implement run method');
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable no-restricted-syntax */
|
|
2
|
-
/* eslint-disable guard-for-in */
|
|
3
1
|
import express from 'express';
|
|
4
2
|
|
|
5
3
|
import Base from './Base.js';
|
|
@@ -88,7 +86,6 @@ class AbstractController extends Base {
|
|
|
88
86
|
this.logger.error(
|
|
89
87
|
`Method ${verb} not exist for router. Please check your codebase`,
|
|
90
88
|
);
|
|
91
|
-
// eslint-disable-next-line no-continue
|
|
92
89
|
continue;
|
|
93
90
|
}
|
|
94
91
|
for (const path in routes[verb]) {
|
|
@@ -112,7 +109,6 @@ class AbstractController extends Base {
|
|
|
112
109
|
routeObject.handler
|
|
113
110
|
}' for controller '${this.getConstructorName()}'`,
|
|
114
111
|
);
|
|
115
|
-
// eslint-disable-next-line no-continue
|
|
116
112
|
continue;
|
|
117
113
|
}
|
|
118
114
|
}
|
|
@@ -300,14 +296,12 @@ class AbstractController extends Base {
|
|
|
300
296
|
let realPath = path;
|
|
301
297
|
if (typeof realPath !== 'string') {
|
|
302
298
|
this.logger.error(`Path not a string ${realPath}. Please check it`);
|
|
303
|
-
// eslint-disable-next-line no-continue
|
|
304
299
|
continue;
|
|
305
300
|
}
|
|
306
301
|
if (!realPath.startsWith('/')) {
|
|
307
302
|
method = realPath.split('/')[0]?.toLowerCase();
|
|
308
303
|
if (!method) {
|
|
309
304
|
this.logger.error(`Method not found for ${realPath}`);
|
|
310
|
-
// eslint-disable-next-line no-continue
|
|
311
305
|
continue;
|
|
312
306
|
}
|
|
313
307
|
realPath = realPath.substring(method.length);
|
|
@@ -316,7 +310,6 @@ class AbstractController extends Base {
|
|
|
316
310
|
this.logger.error(
|
|
317
311
|
`Method ${method} not exist for middleware. Please check your codebase`,
|
|
318
312
|
);
|
|
319
|
-
// eslint-disable-next-line no-continue
|
|
320
313
|
continue;
|
|
321
314
|
}
|
|
322
315
|
const fullPath = `/${httpPath}/${realPath.toUpperCase()}`
|
package/modules/Base.js
CHANGED
|
@@ -21,7 +21,7 @@ class Base {
|
|
|
21
21
|
let l;
|
|
22
22
|
try {
|
|
23
23
|
l = this.#realLogger;
|
|
24
|
-
} catch
|
|
24
|
+
} catch {
|
|
25
25
|
console.warn(
|
|
26
26
|
`You try to accees logger not from class. that can be ok in case of models.`,
|
|
27
27
|
);
|
|
@@ -29,8 +29,9 @@ class Base {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
if (!l) {
|
|
32
|
+
const { loggerGroup } = /** @type {typeof Base} */ (this.constructor);
|
|
32
33
|
this.#realLogger = this.getLogger(
|
|
33
|
-
|
|
34
|
+
loggerGroup + this.getConstructorName(),
|
|
34
35
|
);
|
|
35
36
|
}
|
|
36
37
|
return this.#realLogger;
|
package/modules/BaseCli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
1
|
import path from 'node:path';
|
|
3
2
|
import * as url from 'node:url';
|
|
3
|
+
import { parseArgs } from 'node:util';
|
|
4
4
|
import Base from './Base.js';
|
|
5
5
|
|
|
6
6
|
class Cli extends Base {
|
|
@@ -35,7 +35,6 @@ class Cli extends Base {
|
|
|
35
35
|
console.log('Available commands:');
|
|
36
36
|
let commandsClasses = [];
|
|
37
37
|
for (const c of commands) {
|
|
38
|
-
// eslint-disable-next-line no-await-in-loop
|
|
39
38
|
commandsClasses.push(import(this.commands[c]));
|
|
40
39
|
// console.log(
|
|
41
40
|
// ` \x1b[36m${c.padEnd(maxLength)}\x1b[0m - ${f.default.description}`,
|
|
@@ -43,14 +42,41 @@ class Cli extends Base {
|
|
|
43
42
|
}
|
|
44
43
|
commandsClasses = await Promise.all(commandsClasses);
|
|
45
44
|
for (const [key, c] of Object.entries(commands)) {
|
|
46
|
-
// eslint-disable-next-line no-await-in-loop
|
|
47
45
|
console.log(
|
|
48
46
|
` \x1b[36m${c.padEnd(maxLength)}\x1b[0m - ${commandsClasses[key].default.description}`,
|
|
49
47
|
);
|
|
50
48
|
}
|
|
49
|
+
console.log(
|
|
50
|
+
'\nUsage (use one of option): \n node cli.js <command> [options] \n npm run cli <command> -- [options]',
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static showHelp(Command, finalArguments) {
|
|
55
|
+
console.log(`\n\x1b[32m${Command.description}\x1b[0m`);
|
|
56
|
+
let output = '';
|
|
57
|
+
|
|
58
|
+
Object.entries(finalArguments).forEach(([key, opt]) => {
|
|
59
|
+
const outputLocal = [];
|
|
60
|
+
outputLocal.push(`\n\x1b[36m --${key} \x1b[0m`);
|
|
61
|
+
if (opt.type !== 'boolean') {
|
|
62
|
+
outputLocal.push(`<${opt.type}>`);
|
|
63
|
+
// flag += `<${opt.type}>`;
|
|
64
|
+
}
|
|
65
|
+
if (opt.required) {
|
|
66
|
+
outputLocal.push('(required)');
|
|
67
|
+
}
|
|
68
|
+
outputLocal.push(`\n \x1b[2m${opt.description}`);
|
|
69
|
+
if (opt.default !== undefined) {
|
|
70
|
+
outputLocal.push(` (default: ${opt.default})`);
|
|
71
|
+
}
|
|
72
|
+
outputLocal.push('\x1b[0m');
|
|
73
|
+
output += outputLocal.join(' ');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
console.log(output);
|
|
51
77
|
}
|
|
52
78
|
|
|
53
|
-
async run(command
|
|
79
|
+
async run(command) {
|
|
54
80
|
await this.loadCommands();
|
|
55
81
|
|
|
56
82
|
if (!command) {
|
|
@@ -66,20 +92,54 @@ class Cli extends Base {
|
|
|
66
92
|
}
|
|
67
93
|
const { default: Command } = await import(this.commands[command]);
|
|
68
94
|
|
|
95
|
+
const defaultArgs = {
|
|
96
|
+
help: {
|
|
97
|
+
type: 'boolean',
|
|
98
|
+
description: 'Show help',
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const finalArguments = {
|
|
103
|
+
...Command.commandArguments,
|
|
104
|
+
...defaultArgs,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const parsedArgs = parseArgs({
|
|
108
|
+
args: process.argv.slice(3), // remove command name
|
|
109
|
+
options: finalArguments,
|
|
110
|
+
tokens: true,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// @ts-ignore
|
|
114
|
+
if (parsedArgs.values.help) {
|
|
115
|
+
Cli.showHelp(Command, finalArguments);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
for (const [key, opt] of Object.entries(finalArguments)) {
|
|
120
|
+
if (opt.required && !parsedArgs.values[key]) {
|
|
121
|
+
console.log(
|
|
122
|
+
`\x1b[31mRequired field not proivded. Please provide "${key}" argument\x1b[0m`,
|
|
123
|
+
);
|
|
124
|
+
Cli.showHelp(Command, finalArguments);
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
69
129
|
if (Command.isShouldInitModels) {
|
|
70
130
|
this.logger.debug(
|
|
71
131
|
`Command ${command} isShouldInitModels called. If you want to skip loading and init models, please set isShouldInitModels to false in tyou command`,
|
|
72
132
|
);
|
|
73
133
|
process.env.MONGO_APP_NAME = Command.getMongoConnectionName(
|
|
74
134
|
command,
|
|
75
|
-
|
|
135
|
+
parsedArgs.values,
|
|
76
136
|
);
|
|
77
137
|
await this.server.initAllModels();
|
|
78
138
|
} else {
|
|
79
139
|
this.logger.debug(`Command ${command} NOT need to isShouldInitModels`);
|
|
80
140
|
}
|
|
81
141
|
|
|
82
|
-
const c = new Command(this.app, this.commands,
|
|
142
|
+
const c = new Command(this.app, this.commands, parsedArgs.values);
|
|
83
143
|
let result = false;
|
|
84
144
|
|
|
85
145
|
result = await c.run().catch((e) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaptivestone/framework",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.6",
|
|
4
4
|
"description": "Adaptive stone node js framework",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"test": "vitest run",
|
|
19
19
|
"t": "vitest --coverage=false --reporter=default",
|
|
20
20
|
"prettier": "prettier --check '**/*.(js|jsx|ts|tsx|json|css|scss|md)'",
|
|
21
|
-
"lint": "eslint
|
|
22
|
-
"lint:fix": "eslint
|
|
21
|
+
"lint": "eslint",
|
|
22
|
+
"lint:fix": "eslint --fix",
|
|
23
23
|
"codestyle": "npm run prettier && npm run lint",
|
|
24
24
|
"prepare": "husky",
|
|
25
25
|
"cli": "node cliCommand",
|
|
@@ -37,7 +37,6 @@
|
|
|
37
37
|
"i18next": "^24.0.0",
|
|
38
38
|
"i18next-fs-backend": "^2.0.0",
|
|
39
39
|
"juice": "^11.0.0",
|
|
40
|
-
"minimist": "^1.2.5",
|
|
41
40
|
"mongoose": "^8.0.0",
|
|
42
41
|
"nodemailer": "^6.6.3",
|
|
43
42
|
"nodemailer-sendmail-transport": "^1.0.2",
|
|
@@ -50,12 +49,15 @@
|
|
|
50
49
|
"yup": "^1.0.0"
|
|
51
50
|
},
|
|
52
51
|
"devDependencies": {
|
|
52
|
+
"@eslint/js": "^9.19.0",
|
|
53
|
+
"@types/node": "^22.13.1",
|
|
53
54
|
"@vitest/coverage-v8": "^3.0.0",
|
|
54
|
-
"eslint": "^
|
|
55
|
-
"eslint
|
|
55
|
+
"@vitest/eslint-plugin": "^1.1.25",
|
|
56
|
+
"eslint": "^9.0.0",
|
|
56
57
|
"eslint-config-prettier": "^10.0.0",
|
|
58
|
+
"eslint-plugin-import-x": "^4.6.1",
|
|
57
59
|
"eslint-plugin-prettier": "^5.0.0",
|
|
58
|
-
"
|
|
60
|
+
"globals": "^15.14.0",
|
|
59
61
|
"husky": "^9.0.0",
|
|
60
62
|
"lint-staged": "^15.0.0",
|
|
61
63
|
"mongodb-memory-server": "^10.0.0",
|
package/server.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import Cache from './services/cache/Cache';
|
|
|
10
10
|
import winston from 'winston';
|
|
11
11
|
|
|
12
12
|
import HttpServer from './services/http/HttpServer.js';
|
|
13
|
+
import ControllerManager from './services/http/ControllerManager.js';
|
|
13
14
|
|
|
14
15
|
type ServerConfig = {
|
|
15
16
|
folders: ExpandDeep<TFolderConfig>;
|
|
@@ -27,7 +28,7 @@ declare class Server {
|
|
|
27
28
|
get cache(): Server['cacheService'];
|
|
28
29
|
get logger(): winston.Logger;
|
|
29
30
|
httpServer: HttpServer | null;
|
|
30
|
-
controllerManager: null;
|
|
31
|
+
controllerManager: ControllerManager | null;
|
|
31
32
|
};
|
|
32
33
|
cacheService: Cache;
|
|
33
34
|
|
|
@@ -92,7 +93,7 @@ declare class Server {
|
|
|
92
93
|
/**
|
|
93
94
|
* Run cli command into framework (http, ws, etc)
|
|
94
95
|
*/
|
|
95
|
-
runCliCommand(commandName: string, args
|
|
96
|
+
runCliCommand(commandName: string, args?: {}): Promise<BaseCli['run']>;
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
export default Server;
|
package/server.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
1
|
import EventEmitter from 'node:events';
|
|
3
2
|
import { hrtime, loadEnvFile } from 'node:process';
|
|
4
3
|
import * as url from 'node:url';
|
|
@@ -12,7 +11,7 @@ import Cache from './services/cache/Cache.js';
|
|
|
12
11
|
|
|
13
12
|
try {
|
|
14
13
|
loadEnvFile();
|
|
15
|
-
} catch
|
|
14
|
+
} catch {
|
|
16
15
|
console.warn('No env file found. This is ok. But please check youself.');
|
|
17
16
|
}
|
|
18
17
|
|
|
@@ -323,7 +322,7 @@ class Server {
|
|
|
323
322
|
function IsConstructor(f) {
|
|
324
323
|
try {
|
|
325
324
|
Reflect.construct(String, [], f);
|
|
326
|
-
} catch
|
|
325
|
+
} catch {
|
|
327
326
|
return false;
|
|
328
327
|
}
|
|
329
328
|
return true;
|
|
@@ -415,7 +414,7 @@ class Server {
|
|
|
415
414
|
* @param {String} commandName name of command to load
|
|
416
415
|
* @param {Object} args list of arguments to pass into command
|
|
417
416
|
*/
|
|
418
|
-
async runCliCommand(commandName, args) {
|
|
417
|
+
async runCliCommand(commandName, args = {}) {
|
|
419
418
|
if (!this.cli) {
|
|
420
419
|
const { default: BaseCli } = await import('./modules/BaseCli.js'); // Speed optimisation
|
|
421
420
|
this.cli = new BaseCli(this);
|
package/services/cache/Cache.js
CHANGED
|
@@ -43,7 +43,6 @@ class HttpServer extends Base {
|
|
|
43
43
|
// eslint-disable-next-line no-unused-vars
|
|
44
44
|
this.express.use((err, req, res, next) => {
|
|
45
45
|
// error handling
|
|
46
|
-
// eslint-disable-next-line no-console
|
|
47
46
|
console.error(err.stack);
|
|
48
47
|
// TODO
|
|
49
48
|
res.status(500).json({ message: 'Something broke!' });
|
|
@@ -55,15 +54,15 @@ class HttpServer extends Base {
|
|
|
55
54
|
httpConfig.port,
|
|
56
55
|
httpConfig.hostname,
|
|
57
56
|
() => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
);
|
|
61
|
-
if (+
|
|
57
|
+
const address = listener.address();
|
|
58
|
+
const port = typeof address === 'string' ? 0 : address.port;
|
|
59
|
+
this.logger.info(`App started and listening on port ${port}`);
|
|
60
|
+
if (+port !== +httpConfig.port) {
|
|
62
61
|
// in case we using port 0
|
|
63
|
-
this.app.updateConfig('http', { port
|
|
62
|
+
this.app.updateConfig('http', { port });
|
|
64
63
|
this.logger.info(
|
|
65
64
|
`Updating http config to use new port ${
|
|
66
|
-
|
|
65
|
+
port
|
|
67
66
|
}. Old was ${httpConfig.port} `,
|
|
68
67
|
);
|
|
69
68
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { object } from 'yup';
|
|
2
2
|
import Base from '../../../modules/Base.js';
|
|
3
3
|
|
|
4
4
|
class AbstractMiddleware extends Base {
|
|
@@ -18,13 +18,13 @@ class AbstractMiddleware extends Base {
|
|
|
18
18
|
// eslint-disable-next-line class-methods-use-this
|
|
19
19
|
get relatedQueryParameters() {
|
|
20
20
|
// For example yup.object().shape({page: yup.number().required(),limit: yup.number()})
|
|
21
|
-
return
|
|
21
|
+
return object().shape({});
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// eslint-disable-next-line class-methods-use-this
|
|
25
25
|
get relatedRequestParameters() {
|
|
26
26
|
// For example yup.object().shape({page: yup.number().required(),limit: yup.number()})
|
|
27
|
-
return
|
|
27
|
+
return object().shape({});
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
get relatedReqParameters() {
|
|
@@ -27,6 +27,7 @@ class I18n extends AbstractMiddleware {
|
|
|
27
27
|
if (I18NConfig.enabled) {
|
|
28
28
|
this.logger.info('Enabling i18n support');
|
|
29
29
|
this.i18n = i18next;
|
|
30
|
+
// eslint-disable-next-line import-x/no-named-as-default-member
|
|
30
31
|
i18next.use(BackendFS).init({
|
|
31
32
|
backend: {
|
|
32
33
|
loadPath: `${this.app.foldersConfig.locales}/{{lng}}/{{ns}}.json`,
|
|
@@ -98,7 +99,6 @@ class I18n extends AbstractMiddleware {
|
|
|
98
99
|
for (const detectorName of this.detectorOrder) {
|
|
99
100
|
const lng = this.detectors[detectorName](req);
|
|
100
101
|
if (!lng) {
|
|
101
|
-
// eslint-disable-next-line no-continue
|
|
102
102
|
continue;
|
|
103
103
|
}
|
|
104
104
|
if (i18next.services.languageUtils.isSupportedCode(lng)) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { object, number } from 'yup';
|
|
2
2
|
import AbstractMiddleware from './AbstractMiddleware.js';
|
|
3
3
|
/**
|
|
4
4
|
* Middleware for reusing pagination
|
|
@@ -10,9 +10,9 @@ class Pagination extends AbstractMiddleware {
|
|
|
10
10
|
|
|
11
11
|
// eslint-disable-next-line class-methods-use-this
|
|
12
12
|
get relatedQueryParameters() {
|
|
13
|
-
return
|
|
14
|
-
page:
|
|
15
|
-
limit:
|
|
13
|
+
return object().shape({
|
|
14
|
+
page: number(),
|
|
15
|
+
limit: number(),
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -123,6 +123,7 @@ class Mail extends Base {
|
|
|
123
123
|
this.#renderTemplateFile(templates.style),
|
|
124
124
|
]);
|
|
125
125
|
|
|
126
|
+
// @ts-ignore
|
|
126
127
|
juice.tableElements = ['TABLE'];
|
|
127
128
|
|
|
128
129
|
const juiceResourcesAsync = promisify(juice.juiceResources);
|
|
@@ -139,7 +140,6 @@ class Mail extends Base {
|
|
|
139
140
|
inlinedHTML,
|
|
140
141
|
};
|
|
141
142
|
}
|
|
142
|
-
|
|
143
143
|
/**
|
|
144
144
|
* Send email
|
|
145
145
|
* @param {string} to email send to
|
|
@@ -150,7 +150,7 @@ class Mail extends Base {
|
|
|
150
150
|
async send(to, from = null, aditionalNodemailerOptions = {}) {
|
|
151
151
|
const { subject, text, inlinedHTML } = await this.renderTemplate();
|
|
152
152
|
|
|
153
|
-
return
|
|
153
|
+
return Mail.sendRaw(
|
|
154
154
|
this.app,
|
|
155
155
|
to,
|
|
156
156
|
subject,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { isSchema } from 'yup';
|
|
2
2
|
import YupValidator from './drivers/YupValidator.js';
|
|
3
3
|
import CustomValidator from './drivers/CustomValidator.js';
|
|
4
4
|
import Base from '../../modules/Base.js';
|
|
@@ -7,7 +7,7 @@ class ValidateService extends Base {
|
|
|
7
7
|
constructor(app, validator) {
|
|
8
8
|
super(app);
|
|
9
9
|
this.validator = validator
|
|
10
|
-
?
|
|
10
|
+
? ValidateService.getDriverByValidatorBody(app, validator)
|
|
11
11
|
: null;
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -30,7 +30,7 @@ class ValidateService extends Base {
|
|
|
30
30
|
if (this.isValidatorExists(body)) {
|
|
31
31
|
return body;
|
|
32
32
|
}
|
|
33
|
-
if (
|
|
33
|
+
if (isSchema(body)) {
|
|
34
34
|
const yupValidator = new YupValidator(app, body);
|
|
35
35
|
return yupValidator;
|
|
36
36
|
}
|
|
@@ -104,7 +104,7 @@ class ValidateService extends Base {
|
|
|
104
104
|
const result = [];
|
|
105
105
|
|
|
106
106
|
for (const validator of validators) {
|
|
107
|
-
const formatedValidator =
|
|
107
|
+
const formatedValidator = ValidateService.getDriverByValidatorBody(
|
|
108
108
|
this.app,
|
|
109
109
|
validator,
|
|
110
110
|
);
|
|
@@ -18,8 +18,8 @@ class AbstractValidator extends Base {
|
|
|
18
18
|
return {};
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
// eslint-disable-next-line class-methods-use-this
|
|
22
|
-
async validateFields() {
|
|
21
|
+
// eslint-disable-next-line class-methods-use-this, no-unused-vars
|
|
22
|
+
async validateFields(data, { query, body, appInfo }) {
|
|
23
23
|
// IMPLENT;
|
|
24
24
|
return true;
|
|
25
25
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ValidationError } from 'yup';
|
|
2
2
|
import AbstractValidator from './AbstractValidator.js';
|
|
3
3
|
|
|
4
4
|
class CustomValidator extends AbstractValidator {
|
|
@@ -19,7 +19,7 @@ class CustomValidator extends AbstractValidator {
|
|
|
19
19
|
} catch (e) {
|
|
20
20
|
this.logger.warn(`CustomValidator validateFields ${e}`);
|
|
21
21
|
if (e.path) {
|
|
22
|
-
throw new
|
|
22
|
+
throw new ValidationError({
|
|
23
23
|
[e.path]: e.message,
|
|
24
24
|
});
|
|
25
25
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ValidationError } from 'yup';
|
|
2
2
|
import AbstractValidator from './AbstractValidator.js';
|
|
3
3
|
|
|
4
4
|
class YupValidator extends AbstractValidator {
|
|
5
5
|
get fieldsInJsonFormat() {
|
|
6
|
-
return
|
|
6
|
+
return YupValidator.convertFieldsToJson(this.body);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
static convertFieldsToJson(fields) {
|
|
@@ -76,7 +76,7 @@ class YupValidator extends AbstractValidator {
|
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
throw new
|
|
79
|
+
throw new ValidationError({
|
|
80
80
|
...errorAnswer,
|
|
81
81
|
});
|
|
82
82
|
}
|
package/tests/setup.js
CHANGED
|
@@ -12,7 +12,9 @@ mongoose.set('autoIndex', false);
|
|
|
12
12
|
|
|
13
13
|
let mongoMemoryServerInstance;
|
|
14
14
|
|
|
15
|
+
// @ts-ignore
|
|
15
16
|
jest.setTimeout(1000000);
|
|
17
|
+
// @ts-ignore
|
|
16
18
|
beforeAll(async () => {
|
|
17
19
|
mongoMemoryServerInstance = await MongoMemoryReplSet.create({
|
|
18
20
|
// binary: { version: '4.4.6' },
|
|
@@ -64,9 +66,7 @@ beforeAll(async () => {
|
|
|
64
66
|
nick: 'testUserNickName',
|
|
65
67
|
},
|
|
66
68
|
}).catch((e) => {
|
|
67
|
-
// eslint-disable-next-line no-console
|
|
68
69
|
console.error(e);
|
|
69
|
-
// eslint-disable-next-line no-console
|
|
70
70
|
console.info(
|
|
71
71
|
'That error can happens in case you have custom user model. Please use global.testSetup.disableUserCreate flag to skip user creating',
|
|
72
72
|
);
|
|
@@ -79,6 +79,7 @@ beforeAll(async () => {
|
|
|
79
79
|
await global.server.startServer();
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
+
// @ts-ignore
|
|
82
83
|
beforeEach(() => {
|
|
83
84
|
if (global.server) {
|
|
84
85
|
const key = `test-${Math.random().toString(36).substring(7)}`;
|
|
@@ -88,6 +89,7 @@ beforeEach(() => {
|
|
|
88
89
|
}
|
|
89
90
|
});
|
|
90
91
|
|
|
92
|
+
// @ts-ignore
|
|
91
93
|
afterEach(async () => {
|
|
92
94
|
if (global.server) {
|
|
93
95
|
const { url, namespace } = global.server.getConfig('redis');
|
|
@@ -97,12 +99,13 @@ afterEach(async () => {
|
|
|
97
99
|
await redisClient.connect();
|
|
98
100
|
await clearRedisNamespace(redisClient, namespace);
|
|
99
101
|
await redisClient.disconnect();
|
|
100
|
-
} catch
|
|
102
|
+
} catch {
|
|
101
103
|
// that ok. No redis connection
|
|
102
104
|
}
|
|
103
105
|
}
|
|
104
106
|
});
|
|
105
107
|
|
|
108
|
+
// @ts-ignore
|
|
106
109
|
afterAll(async () => {
|
|
107
110
|
if (global.server) {
|
|
108
111
|
global.server.app.httpServer.shutdown();
|
package/tests/setupVitest.js
CHANGED
|
@@ -57,9 +57,7 @@ beforeAll(async () => {
|
|
|
57
57
|
nick: 'testUserNickName',
|
|
58
58
|
},
|
|
59
59
|
}).catch((e) => {
|
|
60
|
-
// eslint-disable-next-line no-console
|
|
61
60
|
console.error(e);
|
|
62
|
-
// eslint-disable-next-line no-console
|
|
63
61
|
console.info(
|
|
64
62
|
'That error can happens in case you have custom user model. Please use global.testSetup.disableUserCreate flag to skip user creating',
|
|
65
63
|
);
|
|
@@ -90,7 +88,7 @@ afterEach(async () => {
|
|
|
90
88
|
await redisClient.connect();
|
|
91
89
|
await clearRedisNamespace(redisClient, namespace);
|
|
92
90
|
await redisClient.disconnect();
|
|
93
|
-
} catch
|
|
91
|
+
} catch {
|
|
94
92
|
// that ok. No redis connection
|
|
95
93
|
}
|
|
96
94
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// this is from nodejs types
|
|
2
|
+
interface ParseArgsOptionConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Type of argument.
|
|
5
|
+
*/
|
|
6
|
+
type: 'string' | 'boolean';
|
|
7
|
+
/**
|
|
8
|
+
* Whether this option can be provided multiple times.
|
|
9
|
+
* If `true`, all values will be collected in an array.
|
|
10
|
+
* If `false`, values for the option are last-wins.
|
|
11
|
+
* @default false.
|
|
12
|
+
*/
|
|
13
|
+
multiple?: boolean | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* A single character alias for the option.
|
|
16
|
+
*/
|
|
17
|
+
short?: string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* The default option value when it is not set by args.
|
|
20
|
+
* It must be of the same type as the the `type` property.
|
|
21
|
+
* When `multiple` is `true`, it must be an array.
|
|
22
|
+
* @since v18.11.0
|
|
23
|
+
*/
|
|
24
|
+
default?: string | boolean | string[] | boolean[] | undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface ParseArgsOptionsConfigExtended extends ParseArgsOptionConfig {
|
|
28
|
+
/**
|
|
29
|
+
* A description of the option.
|
|
30
|
+
*/
|
|
31
|
+
description?: string;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Is it required?
|
|
35
|
+
*/
|
|
36
|
+
required?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ICommandArguments {
|
|
40
|
+
[longOption: string]: ParseArgsOptionsConfigExtended;
|
|
41
|
+
}
|
package/vitest.config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// eslint-disable-next-line import/no-unresolved
|
|
1
|
+
// eslint-disable-next-line import-x/no-unresolved
|
|
2
2
|
import { defineConfig } from 'vitest/config';
|
|
3
3
|
|
|
4
4
|
export default defineConfig({
|
|
@@ -9,6 +9,7 @@ export default defineConfig({
|
|
|
9
9
|
outputFile: './coverage/rspec.xml',
|
|
10
10
|
reporters: ['default', 'junit'],
|
|
11
11
|
coverage: {
|
|
12
|
+
provider: 'v8',
|
|
12
13
|
enabled: true,
|
|
13
14
|
reporter: ['text', 'html', 'clover', 'json', 'cobertura'],
|
|
14
15
|
},
|