namirasoft-node 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/AnomalyDetector.d.ts +26 -0
  2. package/dist/AnomalyDetector.js +74 -0
  3. package/dist/AnomalyDetector.js.map +1 -0
  4. package/dist/BaseApplication.d.ts +22 -0
  5. package/dist/BaseApplication.js +104 -0
  6. package/dist/BaseApplication.js.map +1 -0
  7. package/dist/BaseController.d.ts +15 -0
  8. package/dist/BaseController.js +11 -0
  9. package/dist/BaseController.js.map +1 -0
  10. package/dist/BaseDatabase.d.ts +4 -0
  11. package/dist/BaseDatabase.js +7 -0
  12. package/dist/BaseDatabase.js.map +1 -0
  13. package/dist/BaseMiddleware.d.ts +14 -0
  14. package/dist/BaseMiddleware.js +108 -0
  15. package/dist/BaseMiddleware.js.map +1 -0
  16. package/dist/BaseMySqlDatabase.d.ts +4 -0
  17. package/dist/BaseMySqlDatabase.js +11 -0
  18. package/dist/BaseMySqlDatabase.js.map +1 -0
  19. package/dist/BaseSequelizeDatabase.d.ts +15 -0
  20. package/dist/BaseSequelizeDatabase.js +120 -0
  21. package/dist/BaseSequelizeDatabase.js.map +1 -0
  22. package/dist/BaseSequelizeModel.d.ts +3 -0
  23. package/dist/BaseSequelizeModel.js +8 -0
  24. package/dist/BaseSequelizeModel.js.map +1 -0
  25. package/dist/BaseSequelizeTable.d.ts +8 -0
  26. package/dist/BaseSequelizeTable.js +11 -0
  27. package/dist/BaseSequelizeTable.js.map +1 -0
  28. package/dist/BaseTable.d.ts +5 -0
  29. package/dist/BaseTable.js +10 -0
  30. package/dist/BaseTable.js.map +1 -0
  31. package/dist/EmailService.d.ts +16 -0
  32. package/dist/EmailService.js +81 -0
  33. package/dist/EmailService.js.map +1 -0
  34. package/dist/EnvService.d.ts +6 -0
  35. package/dist/EnvService.js +18 -0
  36. package/dist/EnvService.js.map +1 -0
  37. package/dist/IPOperation.d.ts +7 -0
  38. package/dist/IPOperation.js +39 -0
  39. package/dist/IPOperation.js.map +1 -0
  40. package/dist/Meta.d.ts +19 -0
  41. package/dist/Meta.js +29 -0
  42. package/dist/Meta.js.map +1 -0
  43. package/dist/RequestHeaderService.d.ts +8 -0
  44. package/dist/RequestHeaderService.js +17 -0
  45. package/dist/RequestHeaderService.js.map +1 -0
  46. package/dist/ServerToServerOperation.d.ts +5 -0
  47. package/dist/ServerToServerOperation.js +24 -0
  48. package/dist/ServerToServerOperation.js.map +1 -0
  49. package/dist/index.d.ts +15 -0
  50. package/dist/index.js +32 -0
  51. package/dist/index.js.map +1 -0
  52. package/package.json +33 -0
  53. package/src/AnomalyDetector.ts +85 -0
  54. package/src/BaseApplication.ts +112 -0
  55. package/src/BaseController.ts +16 -0
  56. package/src/BaseDatabase.ts +5 -0
  57. package/src/BaseMiddleware.ts +121 -0
  58. package/src/BaseMySqlDatabase.ts +8 -0
  59. package/src/BaseSequelizeDatabase.ts +132 -0
  60. package/src/BaseSequelizeModel.ts +7 -0
  61. package/src/BaseSequelizeTable.ts +13 -0
  62. package/src/BaseTable.ts +10 -0
  63. package/src/EmailService.ts +97 -0
  64. package/src/EnvService.ts +18 -0
  65. package/src/IPOperation.ts +39 -0
  66. package/src/Meta.ts +35 -0
  67. package/src/RequestHeaderService.ts +19 -0
  68. package/src/ServerToServerOperation.ts +24 -0
  69. package/src/index.ts +15 -0
  70. package/tsconfig.json +30 -0
@@ -0,0 +1,121 @@
1
+ import * as express from 'express';
2
+ import { BaseDatabase } from './BaseDatabase';
3
+ import { Meta } from './Meta';
4
+ import Joi from 'joi';
5
+ import { AnomalyDetector } from './AnomalyDetector';
6
+ import { BaseController } from './BaseController';
7
+ import { ErrorOperation, HTTPError } from 'namirasoft-core';
8
+ import { ILogger } from "namirasoft-log";
9
+
10
+ export abstract class BaseMiddleware<D extends BaseDatabase, State, Props>
11
+ {
12
+ protected logger: ILogger;
13
+ abstract getDatabase(): Promise<D>;
14
+ abstract getLogger(): ILogger;
15
+ abstract preHandle(req: express.Request, res: express.Response, database: D, props: Props): Promise<void>;
16
+ abstract postHandle(req: express.Request, res: express.Response, database: D, props: Props): Promise<void>;
17
+ abstract getProps(req: express.Request, res: express.Response, database: D, state: State): Promise<Props>;
18
+ constructor()
19
+ {
20
+ this.logger = this.getLogger();
21
+ }
22
+ getHandler(controller: BaseController<D, State, Props>)
23
+ {
24
+ return async (req: express.Request, res: express.Response) =>
25
+ {
26
+ // meta
27
+ let meta = new Meta(req);
28
+ // result
29
+ let result = {};
30
+ try
31
+ {
32
+ // meta
33
+ meta.onStart();
34
+ if (controller.showLogAtTheBeginning)
35
+ this.logger.info(JSON.stringify(meta));
36
+ // init controller
37
+ let database = await this.getDatabase();
38
+ let state = controller.getState();
39
+ let props = await this.getProps(req, res, database, state);
40
+
41
+ // preHandle
42
+ await this.preHandle(req, res, database, props);
43
+ await controller.preHandle(req, res, database, props);
44
+
45
+ // check for anomaly
46
+ let anomaly: AnomalyDetector | null = controller.getAnomaly();
47
+ if (anomaly != null)
48
+ if (anomaly.isAnomaly(meta.ip, meta.url))
49
+ ErrorOperation.throwHTTP(403, 'Suspicious activity detected.');
50
+
51
+ // check body validation
52
+ let bodySchema = controller.getBodySchema();
53
+ if (bodySchema != null)
54
+ {
55
+ const validation = await Joi.compile(bodySchema)
56
+ .prefs({ errors: { label: 'key' } })
57
+ .validate(req.body);
58
+ if (validation.error)
59
+ {
60
+ let message = validation.error.details.map((details) => details.message).join(', ');
61
+ ErrorOperation.throwHTTP(400, message);
62
+ }
63
+ }
64
+ // check query validation
65
+ let querySchema = controller.getQuerySchema();
66
+ if (querySchema != null)
67
+ {
68
+ const validation = await Joi.compile(querySchema)
69
+ .prefs({ errors: { label: 'key' } })
70
+ .validate(req.query);
71
+ if (validation.error)
72
+ {
73
+ let message = validation.error.details.map((details) => details.message).join(', ');
74
+ ErrorOperation.throwHTTP(400, message);
75
+ }
76
+ }
77
+
78
+ // call controller
79
+ if (controller.handle)
80
+ result = await controller.handle(req, res, database, props);
81
+ if (result == null)
82
+ result = "Success";
83
+
84
+ // postHandle
85
+ await controller.postHandle(req, res, database, props);
86
+ await this.postHandle(req, res, database, props);
87
+ } catch (error)
88
+ {
89
+ let message: string;
90
+ if (error instanceof Error)
91
+ {
92
+ meta.error = error;
93
+ message = error.message;
94
+ }
95
+ else
96
+ message = error + "";
97
+
98
+ if (error instanceof HTTPError)
99
+ {
100
+ meta.code = error.code;
101
+ this.logger.error(error.message + "\n" + JSON.stringify(meta), undefined, error.stack);
102
+ }
103
+ else
104
+ {
105
+ meta.code = 500;
106
+ if (error instanceof Error)
107
+ this.logger.critical(error.message + "\n" + JSON.stringify(meta), undefined, error.stack);
108
+ }
109
+ meta.message = message;
110
+ if (error instanceof HTTPError)
111
+ result = meta.message;
112
+ else
113
+ result = "Sorry, internl server error.";
114
+ }
115
+ meta.onFinish();
116
+ if (controller.showLogAtTheEnd)
117
+ this.logger.info(JSON.stringify(meta));
118
+ return res.status(meta.code).send(result);
119
+ };
120
+ }
121
+ }
@@ -0,0 +1,8 @@
1
+ import { BaseSequelizeDatabase } from "./BaseSequelizeDatabase";
2
+ export abstract class BaseMySqlDatabase extends BaseSequelizeDatabase
3
+ {
4
+ constructor(name: string, user: string, pass: string, host: string, port: number, logging: boolean = false)
5
+ {
6
+ super('mysql', name, user, pass, host, port, logging);
7
+ }
8
+ }
@@ -0,0 +1,132 @@
1
+ import { Sequelize, Dialect, Op } from "sequelize";
2
+ import { Model, ModelCtor, FindOptions, Transaction, ModelAttributes, Attributes, ModelOptions } from 'sequelize';
3
+ import { BaseDatabase } from "./BaseDatabase";
4
+ import { BaseSequelizeModel } from "./BaseSequelizeModel";
5
+ import { ErrorOperation } from "namirasoft-core";
6
+ import { Where } from "sequelize/types/utils";
7
+
8
+ export abstract class BaseSequelizeDatabase extends BaseDatabase
9
+ {
10
+ protected sequelize: Sequelize;
11
+ constructor(dialect: Dialect, name: string, user: string, pass: string, host: string, port: number, logging: boolean = false)
12
+ {
13
+ super();
14
+ this.sequelize = new Sequelize(
15
+ name,
16
+ user,
17
+ pass,
18
+ {
19
+ dialect,
20
+ host,
21
+ port,
22
+ logging
23
+ });
24
+ }
25
+ define<M extends Model, TAttributes = Attributes<M>>(
26
+ modelName: string,
27
+ attributes: ModelAttributes<M, TAttributes>,
28
+ options?: ModelOptions<M>
29
+ ): ModelCtor<M>
30
+ {
31
+ if (!options)
32
+ options = {};
33
+ if (options.name == undefined)
34
+ options.name = {
35
+ plural: modelName,
36
+ singular: modelName
37
+ };
38
+ if (options.paranoid == undefined)
39
+ options.paranoid = true;
40
+ if (options.freezeTableName == undefined)
41
+ options.freezeTableName = true;
42
+ if (options.tableName == undefined)
43
+ options.tableName = modelName;
44
+ if (options.underscored == undefined)
45
+ options.underscored = true;
46
+ if (options.timestamps == undefined)
47
+ options.timestamps = true;
48
+ if (options.paranoid == undefined)
49
+ options.paranoid = true;
50
+ if (options.createdAt == undefined)
51
+ options.createdAt = true;
52
+ if (options.updatedAt == undefined)
53
+ options.updatedAt = true;
54
+
55
+ return this.sequelize.define(modelName, attributes, options);
56
+ }
57
+ async startTransaction<T>(handler: (trx: Transaction) => Promise<T>, trx: Transaction | null): Promise<T>
58
+ {
59
+ if (trx)
60
+ return await handler(trx);
61
+ trx = await this.sequelize.transaction();
62
+ try
63
+ {
64
+ let result = await handler(trx);
65
+ await trx.commit();
66
+ return result;
67
+ }
68
+ catch (error)
69
+ {
70
+ await trx.rollback();
71
+ throw error;
72
+ }
73
+ }
74
+ async getModel<M extends BaseSequelizeModel>(modelName: string, options: FindOptions<Attributes<M>>, trx: Transaction | null): Promise<M>
75
+ {
76
+ let value = await this.getModelOrNull(modelName, options, trx);
77
+ if (value != null)
78
+ return value;
79
+ throw ErrorOperation.getHTTP(404, "Could not found " + modelName);
80
+ }
81
+ async getModelOrNull<M extends BaseSequelizeModel>(modelName: string, options: FindOptions<Attributes<M>>, trx: Transaction | null): Promise<M | null>
82
+ {
83
+ if (!options)
84
+ options = {};
85
+ options.transaction = trx;
86
+ let model = this.sequelize.models[modelName] as ModelCtor<M>;
87
+ return await model.findOne<M>(options);
88
+ }
89
+ getGoogleSearchConditions(columns: string[], search: string, conditions: Where[])
90
+ {
91
+ if (!conditions)
92
+ conditions = [];
93
+ if (search)
94
+ if (search.split)
95
+ {
96
+ let toks = search.split(' ');
97
+ if (toks.length > 0)
98
+ {
99
+ for (let i = 0; i < toks.length; i++)
100
+ {
101
+ let rOpr = { [Op.like]: '%' + toks[i].trim() + '%' };
102
+ let lOpr;
103
+ let cs = columns.map(column => Sequelize.col(column));
104
+ lOpr = Sequelize.fn(
105
+ "concat",
106
+ ...cs
107
+ );
108
+ let condition = Sequelize.where(lOpr, rOpr);
109
+ conditions.push(condition);
110
+ }
111
+ }
112
+ }
113
+ return conditions;
114
+ }
115
+
116
+ paginate<T extends BaseSequelizeModel>(options: FindOptions<Attributes<T>>,
117
+ page_number: number, page_size: number, page_size_default?: number)
118
+ {
119
+ // page_number
120
+ if (isNaN(page_number))
121
+ page_number = 1;
122
+ // page_size
123
+ if (isNaN(page_size))
124
+ if (page_size_default)
125
+ page_size = page_size_default;
126
+ if (isNaN(page_size))
127
+ page_size = 20;
128
+ //
129
+ options.offset = (page_number - 1) * page_size;
130
+ options.limit = page_size;
131
+ }
132
+ }
@@ -0,0 +1,7 @@
1
+
2
+ import { Model } from 'sequelize';
3
+
4
+ export abstract class BaseSequelizeModel extends Model
5
+ {
6
+
7
+ }
@@ -0,0 +1,13 @@
1
+ import { ModelCtor } from "sequelize";
2
+ import { BaseSequelizeDatabase } from "./BaseSequelizeDatabase";
3
+ import { BaseSequelizeModel } from "./BaseSequelizeModel";
4
+ import { BaseTable } from "./BaseTable";
5
+
6
+ export class BaseSequelizeTable<D extends BaseSequelizeDatabase, M extends BaseSequelizeModel> extends BaseTable<D>
7
+ {
8
+ model!: ModelCtor<M>;
9
+ constructor(database: D)
10
+ {
11
+ super(database);
12
+ }
13
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseDatabase } from "./BaseDatabase";
2
+
3
+ export class BaseTable<D extends BaseDatabase>
4
+ {
5
+ database: D;
6
+ constructor(database: D)
7
+ {
8
+ this.database = database;
9
+ }
10
+ }
@@ -0,0 +1,97 @@
1
+ import nodemailer from 'nodemailer';
2
+ import smtpTransport from 'nodemailer-smtp-transport';
3
+ import Mail, { AttachmentLike } from "nodemailer/lib/mailer";
4
+ import { Readable } from "stream";
5
+
6
+ export class EmailService
7
+ {
8
+ host: string;
9
+ username: string;
10
+ username_from: string;
11
+ password: string;
12
+ error_title: string;
13
+ error_recipients: string;
14
+ constructor(host: string, username: string, username_from: string, password: string, error_title: string, error_recipients: string)
15
+ {
16
+ this.host = host;
17
+ this.username = username;
18
+ this.username_from = username_from;
19
+ this.password = password;
20
+ this.error_title = error_title;
21
+ this.error_recipients = error_recipients;
22
+ }
23
+ sendExeption(error: Error, meta: any, callback?: (err: Error | null, info: any) => void)
24
+ {
25
+ let title = error.message;
26
+ let message = title;
27
+ if (meta)
28
+ message += "\r\n" + JSON.stringify(meta);
29
+ message += "\r\n" + error.stack;
30
+ this.sendError(title, message, callback);
31
+ }
32
+ sendError(title: string, message: string, callback?: (err: Error | null, info: any) => void)
33
+ {
34
+ if (!title)
35
+ title = '';
36
+ let toks = this.error_recipients.split(',');
37
+ for (let i = 0; i < toks.length; i++)
38
+ {
39
+ const email = toks[i];
40
+ this.send(
41
+ email,
42
+ this.error_title + " - " + title,
43
+ message, undefined, callback
44
+ );
45
+ }
46
+ }
47
+ send(to: string, subject: string, text: string, html?: string | Buffer | Readable | AttachmentLike | undefined, callback?: (err: Error | null, info: any) => void)
48
+ {
49
+ if (!this.username)
50
+ return;
51
+ if (!this.password)
52
+ return;
53
+ let transform = {}
54
+ if (this.host === 'gmail')
55
+ transform = smtpTransport({
56
+ service: 'gmail',
57
+ host: 'smtp.gmail.com',
58
+ auth: {
59
+ user: this.username,
60
+ pass: this.password
61
+ }
62
+ });
63
+ else
64
+ transform = {
65
+ host: this.host,
66
+ port: 465,
67
+ secure: true,
68
+ auth: {
69
+ user: this.username,
70
+ pass: this.password
71
+ }
72
+ };
73
+
74
+ let transporter = nodemailer.createTransport(transform);
75
+
76
+ let mailOptions: Mail.Options = {
77
+ from: this.username_from,
78
+ to,
79
+ subject,
80
+ text,
81
+ html
82
+ };
83
+ if (html)
84
+ mailOptions.html = html;
85
+
86
+ transporter.sendMail(mailOptions, function (error, info)
87
+ {
88
+ if (callback)
89
+ callback(error, info);
90
+ else
91
+ {
92
+ if (error)
93
+ console.log(error);
94
+ }
95
+ });
96
+ }
97
+ }
@@ -0,0 +1,18 @@
1
+ import { ConvertService } from "namirasoft-core";
2
+
3
+ export class EnvService extends ConvertService
4
+ {
5
+ name: string;
6
+ constructor(name: string)
7
+ {
8
+ super();
9
+ this.name = name;
10
+ }
11
+ override getNullString(): string | null
12
+ {
13
+ let ans = process.env[this.name];
14
+ if (ans)
15
+ return ans;
16
+ return null;
17
+ }
18
+ }
@@ -0,0 +1,39 @@
1
+ import * as express from 'express';
2
+ import { getClientIp } from '@supercharge/request-ip';
3
+ import { RequestHeaderService } from './RequestHeaderService';
4
+ import { ErrorOperation } from 'namirasoft-core';
5
+
6
+ export class IPOperation
7
+ {
8
+ static ERROR_MESSAGE_IP_IS_NOT_WHITELIST = `Ip does not match the whitelisted IP address: {0}`;
9
+ static getIP(req: express.Request): string
10
+ {
11
+ let ip = new RequestHeaderService(req, 'cf-connecting-ip').getString();
12
+ if (!ip)
13
+ ip = new RequestHeaderService(req, 'x-forwarded-for').getString();
14
+ if (!ip)
15
+ ip = getClientIp(req) ?? "";
16
+ ip = ip.split(',')[0];
17
+ return ip;
18
+ }
19
+ static isWhitelist(req: express.Request, whitelist: string[])
20
+ {
21
+ let ip = this.getIP(req);
22
+ if (!whitelist)
23
+ return true;
24
+ if (whitelist.length === 0)
25
+ return true;
26
+ if (whitelist.includes(ip))
27
+ return true;
28
+ for (let item of whitelist)
29
+ if (ip.substring(0, item.length) === item)
30
+ return true;
31
+ return false;
32
+ }
33
+ static checkWhitelist(req: express.Request, whitelist: string[])
34
+ {
35
+ let valid = this.isWhitelist(req, whitelist);
36
+ if (!valid)
37
+ ErrorOperation.throwHTTP(403, this.ERROR_MESSAGE_IP_IS_NOT_WHITELIST, this.getIP(req));
38
+ }
39
+ }
package/src/Meta.ts ADDED
@@ -0,0 +1,35 @@
1
+ import * as express from 'express';
2
+ import { IncomingHttpHeaders } from "http";
3
+ import { IPOperation } from './IPOperation';
4
+
5
+ export class Meta
6
+ {
7
+ ip: string;
8
+ method: string;
9
+ url: string;
10
+ headers: IncomingHttpHeaders;
11
+ body: any;
12
+ start_time: Date | null = null;
13
+ end_time: Date | null = null;
14
+ duration: number | null = null;
15
+ code: number = 200;
16
+ message: string = "Success";
17
+ error: Error | null = null;
18
+ constructor(req: express.Request)
19
+ {
20
+ this.ip = IPOperation.getIP(req);
21
+ this.method = req.method;
22
+ this.url = req.originalUrl;
23
+ this.headers = req.headers;
24
+ this.body = req.body;
25
+ }
26
+ onStart()
27
+ {
28
+ this.start_time = new Date();
29
+ }
30
+ onFinish()
31
+ {
32
+ this.end_time = new Date();
33
+ this.duration = (this.end_time?.getTime() ?? 0) - (this.start_time?.getTime() ?? 0);
34
+ }
35
+ }
@@ -0,0 +1,19 @@
1
+ import * as express from 'express';
2
+ import { ConvertService, ObjectService } from 'namirasoft-core';
3
+
4
+ export class RequestHeaderService extends ConvertService
5
+ {
6
+ private req: express.Request;
7
+ private name: string;
8
+ constructor(req: express.Request, name: string)
9
+ {
10
+ super();
11
+ this.req = req;
12
+ this.name = name;
13
+ }
14
+ override getNullString()
15
+ {
16
+ let item = this.req.headers[this.name];
17
+ return new ObjectService(item).getNullString();
18
+ }
19
+ }
@@ -0,0 +1,24 @@
1
+ import * as express from 'express';
2
+ import { ErrorOperation, SignOperation } from "namirasoft-core";
3
+ import { RequestHeaderService } from './RequestHeaderService';
4
+
5
+ export class ServerToServerOperation
6
+ {
7
+ static isValid(sign_key: string, data: any, req: express.Request, sign_header: string): boolean
8
+ {
9
+ let signature = new RequestHeaderService(req, sign_header).getString();
10
+ return SignOperation.isValid(sign_key, data, signature);
11
+ }
12
+ static check(sign_key: string, data: any, req: express.Request, sign_header: string): void
13
+ {
14
+ if (!sign_key)
15
+ ErrorOperation.throwHTTP(401, "Invlid signature - No sign key.");
16
+ if (!sign_header)
17
+ ErrorOperation.throwHTTP(401, "Invlid signature - No sign header name.");
18
+ let signature = new RequestHeaderService(req, sign_header).getString();
19
+ if (!signature)
20
+ ErrorOperation.throwHTTP(401, "Invlid signature - No signature.");
21
+ if (!this.isValid(sign_key, data, req, sign_header))
22
+ ErrorOperation.throwHTTP(401, "Invlid signature.");
23
+ }
24
+ }
package/src/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ export * from "./AnomalyDetector";
2
+ export * from "./BaseApplication";
3
+ export * from "./BaseController";
4
+ export * from "./BaseDatabase";
5
+ export * from "./BaseMiddleware";
6
+ export * from "./BaseSequelizeModel";
7
+ export * from "./BaseMySqlDatabase";
8
+ export * from "./BaseSequelizeTable";
9
+ export * from "./BaseSequelizeDatabase";
10
+ export * from "./BaseTable";
11
+ export * from "./EmailService";
12
+ export * from "./EnvService";
13
+ export * from "./IPOperation";
14
+ export * from "./RequestHeaderService";
15
+ export * from "./ServerToServerOperation";
package/tsconfig.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES6",
4
+ "module": "CommonJS",
5
+ "rootDir": "./src",
6
+ "outDir": "./dist",
7
+ "lib": [
8
+ "es6",
9
+ "dom"
10
+ ],
11
+ "allowJs": false,
12
+ "checkJs": false,
13
+ "strict": true,
14
+ "esModuleInterop": true,
15
+ "sourceMap": true,
16
+ "resolveJsonModule": true,
17
+ "declaration": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "noImplicitAny": true,
21
+ "noImplicitOverride": true,
22
+ "noImplicitReturns": true,
23
+ "noImplicitThis": true,
24
+ "skipLibCheck": false,
25
+ "allowSyntheticDefaultImports": true,
26
+ "forceConsistentCasingInFileNames": true,
27
+ "noFallthroughCasesInSwitch": true,
28
+ "isolatedModules": false,
29
+ }
30
+ }