Browse Source

First time commit

HonorLee 1 year ago
commit
6d10bdfcdd

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+node_modules/
+package-lock.json
+dist/
+.DS_Store
+*/.DS_Store

+ 0 - 0
README.md


+ 17 - 0
nginx.conf.example

@@ -0,0 +1,17 @@
+server {
+        listen 8080;
+        server_name {YourServername.com};
+        root /{Your SugarNode path}/dist;
+        location ~.*\.(jpg|png|gif|txt)$ {
+                try_files $uri @proxy;
+        }
+        location /asset/ {
+                add_header Cache-Control no-store;
+                try_files $uri $uri @proxy;
+        }
+        location / {
+                add_header Access-Control-Allow-Origin *;
+                proxy_pass http://127.0.0.1:8080;
+                client_max_body_size 20m;
+        }
+}

+ 48 - 0
package.json

@@ -0,0 +1,48 @@
+{
+  "name": "sugarnode-ts",
+  "version": "1.0.0",
+  "description": "A fast-easy way to learn or use nodejs to create your own (web) server",
+  "main": "main.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1",
+    "build": "tsc",
+    "build-example": "tsc -p tsconfigExample.json",
+    "clean": "tsc --build --clean",
+    "watch": "tsc --watch",
+    "start": "node dist/main.js"
+  },
+  "keywords": [
+    "Node",
+    "NodeJs",
+    "MVC",
+    "Syntactic",
+    "sugar"
+  ],
+  "author": "HonorLee",
+  "license": "MIT",
+  "devDependencies": {
+    "@types/formidable": "^2.0.5",
+    "@types/memcached": "^2.2.7",
+    "@types/mongodb": "^4.0.7",
+    "@types/mysql": "^2.15.21",
+    "@types/node": "^18.0.0",
+    "@types/redis": "^4.0.11",
+    "@typescript-eslint/eslint-plugin": "^5.29.0",
+    "@typescript-eslint/parser": "^5.29.0",
+    "eslint": "^8.18.0",
+    "typescript": "^4.7.4"
+  },
+  "dependencies": {
+    "colors": "^1.4.0",
+    "ejs": "^3.1.8",
+    "formidable": "^2.0.1",
+    "fs-extra": "^10.1.0",
+    "klaw-sync": "^6.0.0",
+    "memcached": "^2.2.2",
+    "mongodb": "^4.7.0",
+    "mysql": "^2.18.1",
+    "redis": "^4.1.0",
+    "request": "^2.88.2",
+    "tracer": "^1.1.6"
+  }
+}

+ 8 - 0
source/.eslintrc.js

@@ -0,0 +1,8 @@
+module.exports = {
+    'parser':'@typescript-eslint/parser',
+    'extends': ['plugin:@typescript-eslint/recommended'],
+    'env': {'node': true},
+    'rules': {
+        '@typescript-eslint/no-explicit-any':'off'//关闭any类型警告
+    }    
+}

+ 64 - 0
source/@types/global.d.ts

@@ -0,0 +1,64 @@
+import { RedisClientType } from "@redis/client";
+import { IncomingMessage, ServerResponse } from "http";
+
+/**
+ * Global variable
+**/
+declare global{
+    //系统对象
+    let SYSTEM:any;
+    //全局FS对象
+    let FILE:any;
+    //全局Moment
+    let Moment:Moment;
+    //Logger
+    declare namespace LOGGER {
+        function log(msg:string):void;
+        function info(msg:string):void;
+        function debug(msg:string):void;
+        function warn(msg:string):void;
+        function error(msg:string):void;
+    }
+    //数据库配置项接口
+    declare interface DBOption{
+        host:string,
+        port:number,
+        username?:string,
+        password?:string,
+        database?:string,
+        prefix?:string
+    }
+    declare function DBCallback(err:Error,data:any):void;
+    declare function H(helperName:string):any;
+    declare function M(moduleName:string):any;
+    // declare namespace DBManager {
+    //     function create(DatabaseType:string,option:DBOption,GlobalName?:string,callback?:(err:Error|null,conn?:any)=>void);
+    //     /**
+    //      * @param dbname 数据库名称,目前支持: redis | mysql | mongodb | memcache
+    //      */
+    //     function getOption(dbname:string):DBOption;
+    // }
+    // let ROOTPATH:string;
+    // let SYSTEM:object;
+
+    declare interface RequestData{
+        req:IncomingMessage|undefined,
+        res:ServerResponse|undefined,
+        path:string|null,
+        COOKIE:any,
+        GET:any,
+        POST:any,
+        UPLOAD:any
+    }
+    declare class CONTROLLER{
+        end(data:any,status?:number,mime?:string):void;
+        endRedirect(location:string,permanently:boolean);
+        endJSON(somthing:any);
+        endAPI(errorCode:number,somthing:any);
+        endView(viewname:string,data:any,option?:any);
+        setCookie(key:string,value:any,expire?:number|'forever',path?:string);
+        renewCookie(key:string,expire:number|'forever',path?:string);
+    }
+}
+
+export = {}

+ 28 - 0
source/app/main.ts

@@ -0,0 +1,28 @@
+/**
+ * This is entry file when system running.
+ * You can change in /config file
+ */
+
+import { RedisClientType } from "@redis/client";
+
+/**
+ * This one shows how to make a redis connection through database tookit.
+ */
+
+/**
+ * Function 'H' is a syntactic sugar to get util tools quickly.
+ * It will find the tool library automatically in '/lib/helper' or '/app/lib/helper'.
+ */
+export = {};
+declare const MainRedis:RedisClientType;    //For typescript :(
+
+const DBManager = H('database');    //Get database manager tookit
+const redisOption:DBOption = DBManager.getOption('redis');  //Make default connect option for redis
+// redisOption.host = option.Redis.host;    //You can edit the option
+// redisOption.port = option.Redis.port;
+
+DBManager.create('redis',redisOption,'MainRedis',async (err:Error,redisClient:RedisClientType)=>{  //Create a redis database connection & Set a global property named 'MainRedis' that you can use it everywhere,don't forget decalre.
+    LOGGER.log('Test connect redis server');  //Global logger tookit,support [ log | info | debug | warn | error ] 5 levels
+    const info = await MainRedis.info();
+    LOGGER.debug(info);
+});

+ 103 - 0
source/app/mvc/controller/example.ts

@@ -0,0 +1,103 @@
+/**
+ * You can visit URL: 
+ * http://127.0.0.1:8080/example or http://127.0.0.1:8080/example/endWithStatusCode
+ * for test
+ */
+
+export default class Example extends CONTROLLER{
+    private status:string | undefined;
+    /**
+     * This constructor will be called when controller init,you can ignore it if not useful;
+     * DO NOT User function {constructor} for instead!!!
+     */
+    __construct(){
+        this.status = 'SUCCESS';
+    }
+    /**
+     * This method shows how to end a request
+     */
+    index(){
+        this.end(this.status);
+    }
+    /**
+     * End a request with http status code
+     */
+    endWithStatusCode(){
+        this.end(this.status,403);
+    }
+    /**
+     * End a request sepcify different mime type
+     */
+    endWithMimetype(){
+        this.end(this.status,200,'text/html');
+    }
+    /**
+     * End with redirect (permanently or not)
+     */
+    redirect(){
+        /**
+         * Permanently StatusCode:
+         * True:  301
+         * False: 302
+         */
+        const permanently = true;
+        this.endRedirect('/example',permanently);
+    }
+    /**
+     * End with a raw json type string
+     */
+    endRawJson(){
+        // Default end a json string with mimeType 'text/json' response
+        this.endJSON({status:this.status});
+
+        /**
+         * You can just return a string or number type data
+         * Controller will automake json data like {data:{Your input data}}
+         */
+        // this.endJSON(this.status);
+        // this.endJSON(0xff)
+    }
+    /**
+     * End for a api request
+     */
+    endForAPI(){
+        /**
+         * Controller will automake json data like {error:{ErrorCode},data:{Your Data}} and return a 'text/json' mime type response
+         */
+        this.endAPI(0,{status:this.status});
+    }
+    /**
+     * End a html template
+     * Data will be processed by EJS
+     */
+    endWithViewTpl(){
+        //Controller will auto find html template file in '{Your project dist folder}/app/mvc/view' by the given name
+        this.endView('example',{data:this.status});
+    }
+
+    /**
+     * This shows you how to set cookies 
+     */
+    setSomeCookies(){
+        //Set cookie key -> value
+        this.setCookie('status','SUCCESS');
+        //Set cookie with expire time (Hour)
+        this.setCookie('status','SUCCESS',24);
+        //Set cookie with no outdate
+        this.setCookie('status','SUCCESS','forever');
+        //Set cookie path
+        this.setCookie('status','SUCCESS','forever','/');
+        //Don't forget end
+        this.end(this.status);
+    }
+    renewSomeCookie(){
+        //Renew cookie expire time
+        this.renewCookie('status',24);
+        //With forever
+        this.renewCookie('status','forever');
+        //Reset path
+        this.renewCookie('status','forever','/');
+        //Don't forget end
+        this.end(this.status);
+    }
+}

+ 28 - 0
source/config.ts

@@ -0,0 +1,28 @@
+/**
+ * @Author  HonorLee (dev@honorlee.me)
+ * @Version 1.0 (2019-09-17)
+ * @License MIT
+ */
+ const CONFIG = {
+    //入口文件,默认App目录
+    entrance:'main.js',
+    //扩展模块
+    module:[
+        {
+            // MVC框架模块
+            name:'MVC', //自动查找system/module对应名称模块
+            enable:true,
+            // 配置信息,模块初始化时自动传入
+            option:{
+                root_path:'app/mvc',
+                server_port:8080
+            }
+        }
+    ],
+    /**
+     * Log (file) Level: ALL | log | info | debug | warn | error
+     * Logs will write into log/{Level}.{Date}.log
+    */
+    Log_Level:'info',
+};
+SYSTEM.CONFIG = CONFIG;

+ 47 - 0
source/main.ts

@@ -0,0 +1,47 @@
+/**
+ * SugarNode-TS
+ * This is a fast-easy framework with a lot syntactic-sugar for node developers.
+ * It is the 3rd version from my own develop framework,which had been used for some commercial projects,even some high concurrency iot projects.
+ * And now i decide to port it to TS version.
+ * 
+ * @Author  HonorLee (dev@honorlee.me)
+ * @Version 1.0 (2022-06-25)
+ * @License MIT
+ */
+
+/*
+ * Entry File
+ * DO NOT CHANGE ANYTHING IN THIS FILE!
+ */
+(global as any).SYSTEM = {};
+SYSTEM.ROOTPATH = __dirname;
+
+require("./config");
+require('./system/core');
+
+if(SYSTEM.CONFIG.entrance){
+    FILE.stat(`${SYSTEM.PATH.App}/${SYSTEM.CONFIG.entrance}`,async (err:Error,stats:any)=>{
+        if(err || !stats.isFile()){
+            LOGGER.error(`Wrong entrance file: ${SYSTEM.CONFIG.entrance}`);
+            LOGGER.error(`Entrance Error: ${err}`);
+        }else{
+            await import(`${SYSTEM.PATH.App}/${SYSTEM.CONFIG.entrance}`);
+            for(const module of SYSTEM.CONFIG.module){
+                if(!module.enable) continue;
+                const moduleName = module.name.toLowerCase();
+                const modulePath = `${SYSTEM.PATH.Module}/${moduleName}/${moduleName}`;
+                try{
+                    // eslint-disable-next-line @typescript-eslint/no-var-requires
+                    const MOD = require(modulePath);
+                    new MOD(module.option)
+                    LOGGER.info(`Module [${moduleName.toUpperCase()}] loaded`);
+                }catch(e:any){
+                    LOGGER.error(`Module [${moduleName.toUpperCase()}] loaded error`);
+                    if(e.stack) LOGGER.error(e.stack);
+                }
+            }
+        }
+    })
+}else{
+    LOGGER.error('Empty entrance option,please check Config.js');
+}

+ 59 - 0
source/system/core.ts

@@ -0,0 +1,59 @@
+// 系统进程ID
+SYSTEM.ID = (Math.ceil(Math.random()*61439+4096)).toString(16).toUpperCase();
+
+//系统目录配置
+SYSTEM.PATH = {
+    App         : SYSTEM.ROOTPATH + '/app',
+    System      : SYSTEM.ROOTPATH + '/system',
+    Module      : SYSTEM.ROOTPATH + '/system/module',
+    CoreLib     : SYSTEM.ROOTPATH + '/system/lib/core',
+    ExtraLib    : SYSTEM.ROOTPATH + '/system/lib/extra',
+    CoreHelper  : SYSTEM.ROOTPATH + '/system/lib/helper',
+    Library     : SYSTEM.ROOTPATH + '/lib',
+    Helper      : SYSTEM.ROOTPATH + '/lib/helper',
+    Temp        : SYSTEM.ROOTPATH + '/temp',
+    Log         : SYSTEM.ROOTPATH + '/log'
+}
+
+//全局扩展库
+const Global = (global as any);
+
+Global.FILE          = require('fs-extra');
+Global.FILE.walkSync = require('klaw-sync');
+
+Global.PATH          = require('path');
+Global.REQUEST       = require('request');
+Global.Moment        = require('moment');
+
+//自动载入系统目录运行库
+let CoreLibFiles = FILE.readdirSync(SYSTEM.PATH.CoreLib);
+// console.log(CoreLibFiles)
+CoreLibFiles.forEach((filename:string)=>{
+    const nameWithOutMimeType = (filename.split('.')[0]).toUpperCase();
+    let coreClass;
+    try {
+        coreClass = require(SYSTEM.PATH.CoreLib + '/' + filename);
+
+    }catch(e){
+        console.log('[Core] Core library file ['+filename+'] load error!');
+        console.log(e);
+        Global.Tracer.error('[Core] Core library file ['+filename+'] load error!');
+    }
+    // if(!coreClass) return;
+    if(coreClass.hasOwnProperty && coreClass.hasOwnProperty('_name') && coreClass['_name']){
+        Global[coreClass['_name']] = coreClass;
+    }else{
+        Global[nameWithOutMimeType] = coreClass;
+    }
+    if(typeof coreClass == 'object' && coreClass.hasOwnProperty &&  coreClass.hasOwnProperty('__construct')) coreClass['__construct']();
+});
+CoreLibFiles = null;
+
+//检查并自动创建文件目录
+Object.keys(SYSTEM.PATH).forEach((path)=>{
+    try{
+        FILE.statSync(SYSTEM.PATH[path]);
+    }catch(e){
+        FILE.mkdirsSync(SYSTEM.PATH[path]);
+    }
+});

+ 41 - 0
source/system/lib/core/helper.ts

@@ -0,0 +1,41 @@
+/**
+ * @Author  HonorLee (dev@honorlee.me)
+ * @Version 1.0 (2018-05-04)
+ * @License MIT
+ */
+ module.exports = {
+    __construct:function(){
+        (global as any).H = function(healperName:string){
+            if(!healperName){
+                LOGGER.error('Helper name undefined!!');
+                return null;
+            }
+            healperName = healperName.toLowerCase();
+            const ext = healperName.includes('.js')?'':'.js';
+            let filePath = `${SYSTEM.PATH.Helper}/${healperName}${ext}`
+            try{
+                FILE.statSync(filePath);
+                return require(filePath);
+            }catch(e:any){
+                if(e.code!='ENOENT'){
+                    LOGGER.error('Load help file error:');
+                    LOGGER.error(e.stack);
+                    return;
+                }
+                filePath = `${SYSTEM.PATH.CoreHelper}/${healperName}${ext}`
+                try{
+                    FILE.statSync(filePath);
+                    return require(filePath);
+                }catch(e:any){
+                    if(e.code!='ENOENT'){
+                        LOGGER.error('Load help file error:');
+                        LOGGER.error(e.stack);
+                    }else{
+                        LOGGER.error('No such helper ['+healperName+'],please check the helper file name,all letter must be lowercase');
+                    }
+                    return null;
+                }
+            }
+        }
+    }
+}

+ 77 - 0
source/system/lib/core/logger.ts

@@ -0,0 +1,77 @@
+/**
+ * @Author  HonorLee (dev@honorlee.me)
+ * @Version 1.0 (2018-05-04)
+ * @License MIT
+ */
+
+require('colors');
+import Tracer = require('tracer');
+const TracerInstance = Tracer.dailyfile({root:SYSTEM.PATH.Log,format : `{{timestamp}} | {{file}}:{{line}} {{message}}`, dateformat : "HH:MM:ss.L"});
+
+enum Level{
+    LOG,
+    INFO,
+    DEBUG,
+    WARN,
+    ERROR
+}
+class Logger{
+    public print(level:Level,msg:any,color:string|null):void{
+        // console.log(new Error('test').stack)
+        const str = `[${SYSTEM.ID}][${Level[level]}][${Moment().format('HH:mm:ss')}] ${msg}`;
+        if(color){
+            console.log(str[color as any]);
+        }else{
+            console.log(str);
+        }
+        const WriteLevel = SYSTEM.CONFIG.Log_Level.toUpperCase();
+        if(Level[WriteLevel]){
+            const wlevel = <any>Level[WriteLevel] as number;
+            if(level >= wlevel){
+                this.write(level,msg)
+            }
+        }
+    }
+
+    public write(level:Level,msg:any):void{
+        const fun:string = Level[level].toLowerCase();
+        TracerInstance[fun](msg);
+    }
+
+}
+const LoggerInstance = new Logger();
+const LoggerBridge = new Proxy(LoggerInstance,{
+    get(target:Logger,key:string){
+        let level:Level = -1;
+        let color:string|null = null;
+        switch(key){
+            case 'log':
+                level = Level.LOG;
+                break;
+            case 'info':
+                level = Level.INFO;
+                color = 'green';
+                break;
+            case 'debug':
+                level = Level.DEBUG;
+                color = 'magenta';
+               break;
+            case 'warn':
+                level = Level.WARN;
+                color = 'yellow';
+                break;
+            case 'error':
+                level = Level.ERROR;
+                color = 'red';
+                break;
+            default:
+                return null;
+        }
+        return function(msg:string):void{
+            LoggerInstance.print(level,msg,color);
+        }
+    }
+})
+
+module.exports = LoggerBridge;
+ 

+ 164 - 0
source/system/lib/extra/mysqldb.ts

@@ -0,0 +1,164 @@
+/**
+ * @Author  HonorLee (dev@honorlee.me)
+ * @Version 1.0 (2018-05-04)
+ * @License MIT
+ */
+import Mysql = require('mysql');
+interface DBClient{
+    getConnection:(callback:(err:Error,connection:any)=>void|null)=>void;
+}
+//数据对象
+export class MysqlDB{
+    private _pool:DBClient;
+    constructor(option:DBOption){
+        this._pool = new MysqlPool().create(option);
+    }
+    public query(queryStr:string,callback:(err:Error,ret:any,fields:any)=>void){
+        this._pool.getConnection((err,con)=>{
+            if(err) return callback(err,null,null);
+            con.query(queryStr,(err:Error, ret:any, fields:any)=>{
+                if(err){
+                    LOGGER.error(`MysqlDB: "${queryStr}" error`);
+                    if(err.stack) LOGGER.error(err.stack);
+                }
+                con.release();
+                if(callback) callback(err,ret,fields);
+            });
+        });
+    }
+    public querySync(queryStr:string){
+        return new Promise((resolve)=>{
+            this._pool.getConnection((err,con)=>{
+                if(err){
+                    if(err.stack) LOGGER.error(err.stack);
+                    return resolve({err:err,data:[]});
+                }
+                con.query(queryStr,(err:Error, ret:any, fields:any)=>{
+                    con.release();
+                    if(err){
+                        LOGGER.error(`MysqlDB: "${queryStr}" error`);
+                        if(err.stack) LOGGER.error(err.stack);
+                        return resolve({err:err,data:[]});
+                    }
+                    resolve({err:null,data:ret});
+                });
+            });
+        })
+    }
+    objToKVString(obj:any,joinStr?:string,keyPrefix?:string){
+        const strArr:Array<string> = [];
+
+        Object.keys(obj).forEach(key =>{
+            let v = obj[key];
+            if(key=='extraStr'){
+                if(Array.isArray(v)){
+                    strArr.push(v.join(joinStr));
+                }else{
+                    strArr.push(v);
+                }
+            }else{
+                if(typeof(v)=='string'){
+                    if(v[0]=='='){
+                        v = v.substring(1,-1);
+                    }else{
+                        v = `"${v}"`;
+                    }
+                }
+                keyPrefix = keyPrefix?keyPrefix:'';
+                const sqlkey = `${keyPrefix}\`${key}\``;
+                strArr.push(`${sqlkey}=${v}`);
+                
+            }
+        })
+        if(joinStr) return strArr.join(joinStr);
+        return strArr.join(",");
+    }
+    async update(tablename:string,updateObj:any,whereObj:any){
+        if(!tablename || !updateObj) return;
+        let whereRule="";
+        const updateRule = this.objToKVString(updateObj);
+        if(whereObj){
+            whereRule = ` where ${this.objToKVString(whereObj,' and ')}`;
+        }
+        const sqlStr = `update ${tablename} set ${updateRule} ${whereRule}`;
+        return await this.querySync(sqlStr);
+    }
+    async select(tablename:string,queryField?:Array<string>,whereObj?:any){
+        if(!tablename){
+            const err = new Error("Missing table name")
+            if(err.stack) LOGGER.error(err.stack);
+            return {err:err,data:null};
+        }
+        let queryStr = "*",whereStr = "";
+        if(queryField) queryStr = queryField.join(",");
+        if(whereObj) whereStr = ` where ${this.objToKVString(whereObj,' and ')}`;
+
+        const sql = `select ${queryStr} from ${tablename} ${whereStr}`;
+        return await this.querySync(sql);
+    }
+    async insert(tablename:string,insertData:any){
+        if(!tablename){
+            const err = new Error("Missing table name")
+            if(err.stack) LOGGER.error(err.stack);
+            return {err:err,data:null};
+        }
+        if(!insertData){
+            const err = new Error("Missing insertData")
+            if(err.stack) LOGGER.error(err.stack);
+            return {err:err,data:null};
+        }
+        let sql = `insert into ${tablename} `;
+        if(insertData.constructor == Object){
+            sql += `set ${this.objToKVString(insertData)}`;
+        }else if(insertData.constructor == Array && insertData.length>0 && insertData[0].constructor == Object){
+            const fields = Object.keys(insertData[0]);
+            const values = [];
+            for(const data of insertData){
+                const valueArr = [];
+                for(const key of fields){
+                    if(data[key] != null || data[key] != undefined){
+                        if(data[key].constructor == String){
+                            data[key] = `"${data[key]}"`;
+                        }else if(data[key].constructor == Number){
+                            data[key] = Number(data[key]);
+                        }
+                    }
+                    valueArr.push(data[key]);
+                }
+                values.push(`(${valueArr.join(',')})`);
+            }
+            for(const i in fields){
+                fields[i] = `\`${fields[i]}\``;
+            }
+            sql += `(${fields.join(',')}) values ${values.join(',')}`;            
+        }else{
+            const err = new Error("InsertData type not support")
+            if(err.stack) LOGGER.error(err.stack);
+            return {err:err,data:null};
+        }
+        return await this.querySync(sql);
+    }
+}
+
+
+//连接池
+class MysqlPool{
+    private _option:DBOption|null = null;
+    private _pool:any;
+    public Client:DBClient|null = null;
+
+    create(option:DBOption):DBClient{
+        this._option = option;
+        this._pool   = Mysql.createPool(option);
+        this.Client  = {
+            getConnection:(callback)=>{
+                if(!this._pool) throw new Error("Mysql pool not created!");
+                if(!callback || typeof(callback)!='function') throw new Error("Mysql pool get connection lost callback!");
+                this._pool.getConnection((err:any,connection:any)=>{
+                    callback(err,connection);
+                });
+            }
+        }
+        return this.Client;
+    }
+}

+ 201 - 0
source/system/lib/helper/database.ts

@@ -0,0 +1,201 @@
+import {MysqlDB}  from '../extra/mysqldb';
+// const RedisBridge   = require(Core.Path.ExtraLib + '/redisdb.js');
+import MongoDB    from 'mongodb';
+import MemcacheDB from 'memcached';
+import * as RedisDB    from 'redis';
+
+//默认配置
+const DatabaseOption:any = {
+    mysql    : '{"host":"localhost","port":3306,"user":"root","password":"","database":"","prefix":"","connectionLimit":10}',
+    mongodb  : '{"host":"localhost","port":27017,"user":null,"password":"","database":"","prefix":""}',
+    memcache : '{"host":"localhost","port":11211}',
+    redis    : '{"host":"localhost","port":6379,"prefix":null}'
+}
+
+class Database {
+    /**
+     * @param  {String} DatabaseType    数据库类型
+     * @param  {Object} option          数据库配置
+     * @param  {String} GlobalName      自动全局命名
+     * @param  {Function} callback      回调函数
+     */
+    public static create(DatabaseType:string,option:DBOption,GlobalName?:string,callback?:(err:Error|null,conn?:any)=>void){
+        DatabaseType = DatabaseType.toLowerCase();
+        switch(DatabaseType){
+            case 'mysql':
+                Creater._createMysqlConnection(option,GlobalName,callback);
+                break;
+            case 'mongodb':
+                Creater._createMongoConnection(option,GlobalName,callback);
+                break;
+            case 'memcache':
+                Creater._createMemcacheConnection(option,GlobalName,callback);
+                break;
+            case 'redis':
+                Creater._createRedisConnection(option,GlobalName,callback);
+                break;
+            default:
+                if(callback) callback(new Error('Wrong database type!'));
+        }
+    }
+
+    //生成默认配置
+    public static getOption(type:string){
+        type = type.toLowerCase();
+        if(DatabaseOption[type]!=undefined){
+            return JSON.parse(DatabaseOption[type]);
+        }else{
+            return null;
+        }
+    }
+}
+
+
+
+//连接控制器
+class Creater{
+    static _createMysqlConnection (option:DBOption,GlobalName?:string,callback?:(err:Error|null,conn?:any)=>void){
+        const _MysqlDB = new MysqlDB(option);
+        // if(!callback || typeof(callback)!='function') callback = ()=>{return}
+        //连接测试
+        _MysqlDB.query('SELECT VERSION() as version',(err,result,fields?:any)=>{
+            if(err){
+                LOGGER.error('Mysql Connect error,please recheck your config');
+                if(err.stack) LOGGER.error(err.stack);
+                if(callback) callback(err,null);
+                return;
+            }else{
+                LOGGER.info('Mysql Connect success');
+                LOGGER.info(`Mysql Version: ${result[0]['version']} | User: ${option.username} | Database: ${option.database} | GlobalName: ${GlobalName}`);
+                if(GlobalName){
+                    if((global as any)[GlobalName]){
+                        LOGGER.error(`Create global name fail with "${GlobalName}"`);
+                        if(callback) callback(new Error('Duplicate global name'),null);
+                        return;
+                    }else{
+                        (global as any)[GlobalName] = _MysqlDB;
+                    }
+                }
+                if(callback) callback(null,_MysqlDB);
+            }
+        });
+    }
+    static _createMongoConnection(option:DBOption,GlobalName?:string,callback?:(err:Error|null,conn?:any)=>void){
+        const verify = option.username?option.username+':'+option.password+'@':'';
+        const mongoConnect = 'mongodb://' + verify + option.host+':'+option.port+'/'+option.database;
+        // if(!callback) callback = ()=>{return}
+        MongoDB.MongoClient.connect(mongoConnect,(err?:Error,db?:any)=>{
+            if(err) {
+                LOGGER.error('MongoDB connect error!');
+                if(err.stack) LOGGER.error(err.stack);
+                if(callback) callback(err,null);
+                return;
+            }else{
+                LOGGER.info(`Mongodb Connect success | GlobalName: ${GlobalName}`);
+                const _mongoClient = {
+                    db:db,
+                    c:(collection:any)=>{
+                        return db.collection(option.prefix+collection);
+                    }
+                };
+                if(GlobalName){
+                    if((global as any)[GlobalName]){
+                        LOGGER.error(`Create global name fail with "${GlobalName}"`);
+                        if(callback) callback(new Error('Duplicate global name'),null);
+                        return;
+                    }
+                    (global as any)[GlobalName] = _mongoClient;
+                }
+                if(callback) callback(null,_mongoClient);
+            }
+        });
+    }
+    static _createMemcacheConnection(option:DBOption,GlobalName?:string,callback?:(err:Error|null,conn?:any)=>void){
+        const _Memcache  = new MemcacheDB(option.host+':'+option.port);
+        _Memcache.version(function(err,data){
+            if(err){
+                LOGGER.error('Memcache Connect error,please recheck your config');
+                LOGGER.error(err);
+                if(callback) callback(err,null);
+                return;
+            }else{
+                LOGGER.info('Memcache Connect success');
+                LOGGER.info(`Memcache Version: ${data[0]['version']} | GlobalName: ${GlobalName}`);
+                if(GlobalName){
+                    if((global as any)[GlobalName]){
+                        LOGGER.error(`Create global name fail with "${GlobalName}"`);
+                        if(callback) callback(new Error('Duplicate global name'),null);
+                        return;
+                    }else{
+                        (global as any)[GlobalName] = _Memcache;
+                    }
+                }
+                if(callback) callback(null,_Memcache);
+            }
+        });
+    }
+    static async _createRedisConnection(option:DBOption,GlobalName?:string,callback?:(err:Error|null,conn?:any)=>void){
+        let _redisConnStr = `${option.host}:${option.port}`;
+        if(option.username){
+            if(option.password){
+                _redisConnStr = `${option.username}:${option.password}@${_redisConnStr}`;
+            }else{
+                _redisConnStr = `${option.username}@${_redisConnStr}`;
+            }
+        }
+        _redisConnStr = `redis://${_redisConnStr}`;
+        const _redisClient = RedisDB.createClient({url:_redisConnStr,socket:{keepAlive:30000}});
+        _redisClient.on('error',(err:Error)=>{
+            if(err instanceof RedisDB.SocketClosedUnexpectedlyError){
+                LOGGER.error(`Redis [${option.host}:${option.port}] connect has been refused,please recheck your config,and make sure redis server is running!`);
+            }
+        })
+        _redisClient.on('ready',async ()=>{
+            LOGGER.info('Redis Connect success');
+            const infoStr = <string>await _redisClient.info();
+            const infoLine = infoStr.split('\n');
+            const info:any = {};
+            for(const line of infoLine){
+                const lineArr = line.split(':');
+                if(lineArr.length>1) info[lineArr[0]] = lineArr[1];
+            }
+            if(info.redis_version) LOGGER.info(`GlobalName: ${GlobalName} | Redis Version: ${info.redis_version}`);
+            // _redisClient.stream.setKeepAlive(true,30 * 1000);
+
+            if(GlobalName){
+                if((global as any)[GlobalName]){
+                    LOGGER.error(`Create global name fail with "${GlobalName}"`);
+                    if(callback) callback(new Error('Duplicate global name'),null);
+                    return;
+                }else{
+                    (global as any)[GlobalName] = _redisClient;
+                }
+            }
+            if(callback) callback(null,_redisClient);
+        });
+        _redisClient.on('reconnecting',function(e:any){
+            LOGGER.warn('Redis lost connect,reconnecting!');
+        });
+        await _redisClient.connect();
+            
+        //     {port:option.port,host:option.host,socket_keepalive:true,socket_initialdelay:30000,retry_strategy: function (retryData) {
+        //     if (retryData.error && retryData.error.code === 'ECONNREFUSED') {
+                
+        //     }
+        //     if (retryData.total_retry_time > 1000 * 10) {
+        //         LOGGER.error('Redis retry connect time exhausted');
+        //     }
+        //     if (retryData.attempt > 10) {
+        //         LOGGER.error('Redis unknow error');
+        //     }
+        //     // reconnect after
+        //     return Math.min(retryData.attempt * 100, 3000);
+        // },prefix:option.prefix});
+        // if(!callback || typeof(callback)!='function') callback = ()=>{}
+        
+    }
+}
+
+// (global as any).DBManager = Database;
+
+module.exports = Database;

+ 150 - 0
source/system/module/mvc/lib/controller.ts

@@ -0,0 +1,150 @@
+/**
+ * @Author  HonorLee (dev@honorlee.me)
+ * @Version 1.0 (2018-05-04)
+ * @License MIT
+ */
+export default class Controller{
+    private _requestData:RequestData;
+    private _Request:any;
+    private _Response:any;
+    public COOKIE:any;
+    public GET:any;
+    public POST:any;
+    public UPLOAD:any;
+    constructor(requestData:RequestData) {
+        this._requestData = requestData;
+        this._Request  = requestData.req;
+        this._Response = requestData.res;
+        this.COOKIE    = requestData.COOKIE;
+        this.GET       = requestData.GET;
+        this.POST      = requestData.POST;
+        this.UPLOAD    = requestData.UPLOAD;
+
+        if(this.GET._time) delete this.GET._time;
+        if(this.POST._time) delete this.POST._time;
+        // console.log(this._constructor)
+    }
+    /**
+     * [response description]
+     * @return {[type]} [description]
+     */
+    end(data:any,status?:number,mime?:string):void{
+        let endContent = data,statusCode = 200,mimeType = 'text/plain';
+        if(!endContent) endContent = '';
+        if(typeof endContent == 'number') endContent = `${endContent}`;
+
+        if(status) statusCode = status;
+        if(mime) mimeType = mime;
+        
+        if(status) this._Response.statusCode = status;
+        if(mimeType) this._Response.setHeader('Content-Type', `${mimeType}; charset=UTF-8`);
+        try{
+            this._Response.write(endContent);
+            this._Response.end();
+        }catch(e:any){
+            LOGGER.error(e.stack)
+        }
+    }
+    endRedirect(location:string,permanently?:boolean){
+        const status = permanently?301:302;
+        this._Response.writeHead(status,{'Location':location});
+        this._Response.end();
+    }
+    /**
+     * [responseInJSON description]
+     * @param  {[type]} somthing [String,Number,Object]
+     */
+    endJSON(somthing:any){
+        let endContent;
+        if(typeof somthing == 'string' || typeof somthing == 'number'){
+            endContent = {data:somthing};
+        }else if(typeof somthing == 'object'){
+            endContent = somthing;
+        }else{
+            return this._error(new Error('Function endInJSON argument type must be string,number or object'));
+        }
+        this.end(JSON.stringify(endContent),200,'text/json');
+    }
+    /**
+     * [responseAPI description]
+     * @param  {[type]} errorCode [description]
+     * @param  {[type]} somthing   [description]
+     */
+    endAPI(errorCode:number,somthing:any){
+        const endContent = {error:errorCode,data:null};
+        if(typeof somthing == 'string' || typeof somthing == 'number' || typeof somthing == 'object'){
+            endContent.data = somthing;
+            this.end(JSON.stringify(endContent),200,'text/json');
+        }else{
+            this._error(new Error('Function endAPI argument type must be string,number or object'));
+        }
+    }
+
+    /**
+     * [responseAPI description]
+     * @param  {[type]} errorCode [description]
+     * @param  {[type]} somthing   [description]
+     */
+    endView(viewname:string,data:any,option?:any){
+        const viewFilePath = `${SYSTEM.MVC.PATH.VIEW}/${viewname}`;
+        let tpl;
+        try{
+            tpl = FILE.readFileSync(`${viewFilePath}.html`,'utf-8');
+        }catch(e){
+            try{
+                tpl = FILE.readFileSync(`${viewFilePath}.tpl`,'utf-8');
+            }catch(e){ 
+            }   
+        }
+        if(!tpl) return this.end(`View template [${viewname}] not exist!`);
+        const render = SYSTEM.MVC.EJS.compile(tpl,option);
+        const renderedTpl = render(data);
+        this.end(200,renderedTpl,'text/html');
+    }
+    /**
+     * [responseView description]
+     * @param  {[type]} viewName [description]
+     * @param  {[type]} data     [description]
+     * @return {[type]}          [description]
+     */
+    // endView(viewName,data){
+    //     let view = new VIEW(viewName,data);
+    //     if(view && view.html){
+    //         this.end(200,view.html,'text/html');
+    //     }else{
+    //         this.end(502,`View [${viewName}] not found`,'text/plain');
+    //     }
+    // }
+
+    setCookie(key:string,value:any,expireHour?:number|'forever',path?:string){
+        let cookie,originCookie;
+        if(this._Response.hasHeader('Set-Cookie')){
+            originCookie = this._Response.getHeader('Set-Cookie');
+        }
+        if(!originCookie) originCookie = [];
+        if(typeof originCookie == 'string') originCookie = [originCookie];
+        cookie = `${key}=${value}`;
+        if(path) cookie += ';path=' + path;
+        if(expireHour){
+            let expireTime;
+            if(expireHour=='forever'){
+                expireTime = new Date('9999/01/01 00:00:00').toUTCString();
+            }else{
+                expireTime = new Date(new Date().getTime()+Number(expireHour)*1000*60).toUTCString();
+            }
+            cookie += ';expires=' + expireTime;
+        }
+        originCookie.push(cookie);
+        this._Response.setHeader('Set-Cookie',cookie);
+    }
+
+    renewCookie(key:string,expire:number|'forever',path?:string){
+        const originCookieValue = this.COOKIE[key];
+        if(!originCookieValue) return false;
+        this.setCookie(key,originCookieValue,expire,path);
+    }
+    _error(err:any){
+        LOGGER.error(err.stack);
+        this.end(`<center style="font-size:24px">- 403 -</center>`,403,'text/html');
+    }
+}

+ 32 - 0
source/system/module/mvc/lib/module.ts

@@ -0,0 +1,32 @@
+/**
+ * @Author  HonorLee (dev@honorlee.me)
+ * @Version 1.0 (2018-05-04)
+ * @License MIT
+ */
+ export default class Module{
+    public static init(){
+        //定义全局Module载入语法糖
+        (global as any).M = function(moduleName:string){
+            if(!moduleName){
+                LOGGER.error('Module name undefined!!');
+                return null;
+            }
+            moduleName = moduleName.toLowerCase();
+            // let ext = moduleName.includes('.js')?'':'.js';
+            // let filePath = `${_MVCRootPath.module}/${moduleName}${ext}`
+            const filePath = `${SYSTEM.MVC.PATH.MODULE}/${moduleName}`;
+            try{
+                FILE.statSync(filePath);
+                return require(filePath);
+            }catch(e:any){
+                if(e.code!='ENOENT'){
+                    LOGGER.error('Load moduleName file error:');
+                    if(e.stack) LOGGER.error(e.stack);
+                }else{
+                    LOGGER.error('No such module ['+moduleName+'],please check the moduleName file name,all letter must be lowercase');
+                }
+                return null;
+            }
+        }
+    }
+}

+ 116 - 0
source/system/module/mvc/lib/router.ts

@@ -0,0 +1,116 @@
+/*
+ * @Author: HonorLee
+ * @Version 2.0
+ * @LastUpdate 2019/10/11
+*/
+import PATH = require('path');
+
+export default class Router{
+    public static async run(requestData:RequestData){
+        let method  = 'index';
+        let handlerFile;
+        Object.keys(SYSTEM.MVC.ROUTER_MAP).forEach(path=>{
+            if(requestData.path && requestData.path.indexOf(path) == 0) requestData.path = `${SYSTEM.MVC.ROUTER_MAP[path]}${requestData.path.replace(path,'')}`;
+        })
+        if(requestData.path == '/'){
+            handlerFile = '/index.js'
+        }else if(requestData.path && requestData.path.match(/\/$/g)){
+            handlerFile = `${requestData.path}index.js`;
+        }else if(requestData.path){
+            try{
+                FILE.statSync(`${SYSTEM.MVC.PATH.CONTROLLER}${requestData.path}.js`)
+                handlerFile = `${requestData.path}.js`;
+            }catch(e){
+                const pathArr = requestData.path.split('/');
+                method = pathArr.pop() || 'index';
+                const path = pathArr.join('/');
+                try{
+                    FILE.statSync(`${SYSTEM.MVC.PATH.CONTROLLER}${path}.js`)
+                    handlerFile = `${path}.js`;
+                }catch(e){
+                    try{
+                        FILE.statSync(`${SYSTEM.MVC.PATH.CONTROLLER}${path}/index.js`);
+                        handlerFile = `${path}/index.js`;
+                    }catch(e){
+                        handlerFile = null;
+                    }
+                }
+            }
+        }else{
+            handlerFile = null;
+        }
+        if(method == '__construct') return Router._error(403,'Function __construct can\'t be called outside',requestData.res);
+        if(!handlerFile) return Router._error(404,`No such handler [${requestData.path}]`,requestData.res);
+
+        handlerFile = `${SYSTEM.MVC.PATH.CONTROLLER}${handlerFile}`;
+        let handlerClass,handler;
+        try {
+            // eslint-disable-next-line @typescript-eslint/no-var-requires
+            handlerClass = require(handlerFile);
+            if(handlerClass.default) handlerClass = handlerClass.default;
+        }catch(e:any){
+            console.log(e.stack);
+            if(e.code=='MODULE_NOT_FOUND'){
+                Router._error(404,null,requestData.res);
+            }else{
+                Router._error(404,e.stack,requestData.res);
+            }
+            return;
+        }
+        try{
+            handler = new handlerClass(requestData);
+            // const mixHandler = handler;
+        }catch(e:any){
+            return Router._error(403,e.stack,requestData.res);
+        }
+        // if(handler instanceof CONTROLLER){
+        //     mixHandler = handler;
+        // }else{
+        //     const baseClass  = new CONTROLLER(requestData);
+        //     mixHandler = Object.assign(baseClass,handler);
+        // }
+        
+        if(typeof handler[method]==='function'){
+            let returnSometing;
+            if(typeof handler['__construct']==='function'){
+                try{
+                    returnSometing = await handler['__construct'].call(handler);
+                }catch(e:any){
+                    LOGGER.error(`Function __construct in [${handlerFile}] called error:`);
+                    Router._error(403,e.stack,requestData.res);
+                    return;
+                }
+            }
+            try{
+                handler[method].call(handler,returnSometing);
+            }catch(e:any){
+                LOGGER.error(`Function [${method}] in [${handlerFile}] called error:`);
+                Router._error(403,e.stack,requestData.res);
+            }
+        }else{
+            Router._error(404,`No such method [${method}] in handler [${handlerFile}]`,requestData.res);
+        }
+    }
+    private static _error(code:number,errMsg:string|null,res:any){
+        if(errMsg) LOGGER.error(errMsg);
+        res.writeHead(code, {'Content-Type': 'text/html'});
+        res.end(`<center style="font-size:24px">- ${code} -</center>`);
+    }
+    // private static _encodeRequestData(req){
+    //     req._GET = Router._encodeObj(req._GET);
+    //     req._POST = Router._encodeObj(req._POST);
+    // }
+    // private static _encodeObj(obj){
+    //     let newObj = {};
+    //     Object.keys(obj).forEach(key=>{
+    //         if(typeof(obj[key]==='string')){
+    //             newObj[encodeURI(key)] = encodeURI(obj[key]);
+    //         }else{
+    //             newObj[encodeURI(key)] = obj[key];
+    //         }
+    //     })
+    //     return newObj;
+    // }
+}
+
+// module.exports = Router;

+ 99 - 0
source/system/module/mvc/mvc.ts

@@ -0,0 +1,99 @@
+//HTTP服务
+import HTTP = require('http');
+import URL  = require('url');
+
+//表单处理器
+import formidable = require('formidable')
+//路由控制器
+import Router from './lib/router';
+//模型控制器
+import Module from './lib/module';
+import ControllerClass from './lib/controller';
+(global as any).CONTROLLER = ControllerClass;
+
+const IncomingForm = formidable.IncomingForm;
+class MVC {
+    constructor(option:{root_path:string,server_port:number}) {
+        //定义全局MVC框架变量
+        const Root = `${SYSTEM.ROOTPATH}/${option.root_path}`;
+        SYSTEM.MVC = {
+            ROOT:Root,
+            PORT:option.server_port,
+            PATH:{
+                MODULE:`${Root}/module`,
+                CONTROLLER:`${Root}/controller`,
+                VIEW:`${Root}/view`,
+            },
+            ROUTER_MAP:null,
+            EJS:require('ejs')
+        };
+        //初始化Module控制器
+        Module.init();
+        //初始化MVC应用目录
+        Object.keys(SYSTEM.MVC.PATH).forEach((path)=>{
+            try{
+                FILE.statSync(SYSTEM.MVC.PATH[path]);
+            }catch(e:any){
+                if(e.code=='ENOENT'){
+                    FILE.mkdirsSync(SYSTEM.MVC.PATH[path]);
+                }
+            }
+        });
+        //加载路由Map文件或初始化
+        const mapFile = `${SYSTEM.MVC.ROOT}/map.json`;
+        try{
+            FILE.statSync(mapFile);
+            SYSTEM.MVC.ROUTER_MAP = JSON.parse(FILE.readFileSync(mapFile,'UTF-8'));
+        }catch(e:any){
+            if(e.code=='ENOENT'){
+                FILE.writeFileSync(mapFile,'{}','UTF-8');
+            }else{
+                LOGGER.error(`MVC Router map file [${mapFile}] parse error!`);
+            }
+            SYSTEM.MVC.ROUTER_MAP = {};
+        }
+        try{
+            HTTP.createServer(this._serverHandler).listen(SYSTEM.MVC.PORT);
+            LOGGER.info(`Http MVC Server start at port [${SYSTEM.MVC.PORT}]`);
+        }catch(e:any){
+            LOGGER.error(`Http MVC Server start failed at port [${SYSTEM.MVC.PORT}]`);
+            LOGGER.error(e.stack);
+        }
+    }
+    protected _serverHandler(req?:HTTP.IncomingMessage,res?:HTTP.ServerResponse){
+        if(!req) return;
+        const RequestData:RequestData = {
+            req,res,
+            path:'' as string|null,
+            COOKIE:{} as any,
+            GET:{},
+            POST:{},
+            UPLOAD:{}
+        }
+        req.headers.cookie && req.headers.cookie.split(';').forEach((Cookie)=>{
+            const parts:Array<string> = Cookie.split('=');
+            let key = parts.shift();
+            if (key){
+                key = key.trim();
+                const value = parts.join('=').trim();
+                RequestData.COOKIE[key] = value;
+            }
+        });
+        const URLParse = URL.parse(req.url as string,true);
+        if(URLParse){
+            RequestData.path = URLParse.pathname,
+            RequestData.GET = URLParse.query;
+        }
+        if(req.method && req.method.toLowerCase()=='post'){
+            new IncomingForm().parse(req, (err, fields, files)=>{
+                RequestData.POST = fields;
+                RequestData.UPLOAD = files;
+                Router.run(RequestData);
+            });
+        }else{
+            Router.run(RequestData);
+        }
+    }
+}
+
+module.exports = MVC;

+ 105 - 0
tsconfig.json

@@ -0,0 +1,105 @@
+{
+  "compilerOptions": {
+    /* Visit https://aka.ms/tsconfig to read more about this file */
+
+    /* Projects */
+    // "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+    // "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
+    // "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */
+    // "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */
+    // "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
+    // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */
+
+    /* Language and Environment */
+    "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+    // "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+    // "jsx": "preserve",                                /* Specify what JSX code is generated. */
+    // "experimentalDecorators": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */
+    // "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
+    // "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+    // "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+    // "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+    // "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+    // "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
+    // "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */
+    // "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */
+
+    /* Modules */
+    "module": "commonjs",                                /* Specify what module code is generated. */
+    // "rootDir": "./",                                  /* Specify the root folder within your source files. */
+    // "moduleResolution": "node",                       /* Specify how TypeScript looks up a file from a given module specifier. */
+    // "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
+    // "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
+    // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
+    "typeRoots": [
+      "./node_modules/@types","./source/@types"
+    ],                                  /* Specify multiple folders that act like './node_modules/@types'. */
+    // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
+    // "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
+    // "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */
+    // "resolveJsonModule": true,                        /* Enable importing .json files. */
+    // "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
+
+    /* JavaScript Support */
+    // "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+    // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
+    // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+    /* Emit */
+    // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+    // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
+    // "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
+    // "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
+    // "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+    "outDir": "./dist",                                   /* Specify an output folder for all emitted files. */
+    // "removeComments": true,                           /* Disable emitting comments. */
+    // "noEmit": true,                                   /* Disable emitting files from a compilation. */
+    // "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+    // "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types. */
+    // "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+    // "sourceRoot": "./source",                                 /* Specify the root path for debuggers to find the reference source code. */
+    // "mapRoot": "./source",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
+    // "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
+    // "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
+    // "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+    // "newLine": "crlf",                                /* Set the newline character for emitting files. */
+    // "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+    // "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */
+    // "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
+    // "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */
+    // "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
+    // "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+
+    /* Interop Constraints */
+    // "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
+    // "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
+    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+    // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */
+
+    /* Type Checking */
+    "strict": true,                                      /* Enable all strict type-checking options. */
+    // "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+    // "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */
+    // "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+    // "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+    // "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
+    // "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */
+    // "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */
+    // "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
+    // "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
+    // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
+    // "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
+    // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
+    // "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
+    // "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */
+    // "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
+    // "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */
+    // "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
+    // "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */
+
+    /* Completeness */
+    // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
+    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
+  }
+}