[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n# Unix-style newlines with a newline ending every file\n[*]\nend_of_line = lf\ninsert_final_newline = true\n\n\n# Matches multiple files with brace expansion notation\n# Set default charset\n[*.{js,jsx,html,sass}]\ncharset = utf-8\nindent_style = tab\nindent_size = 4\ntrim_trailing_whitespace = true\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\nmodule_test/\ncoverage/\n.DS_Store\n.idea\npackage-lock.json\n.temp/\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Owen Luke\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "\n# Noapi\n\nA high-performance and easy-to-use web API framework for [Node.js](https://nodejs.org). Noapi loads directory \"biz\" as a web API server, each file in it defines and handles an API, so that you can focus on writing business function code. Noapi is simple enough that you just take it \"out of the box\".\n\n## Install\n\n```sh\nnpm install noapi --save\n```\n\n## TRY IT! (in under 3 minutes)\n\n### 0. Initialize a demo project\n\n```sh\nmkdir ./noapi-demo && cd ./noapi-demo\nnpm init -y\nnpm install noapi --save\n```\n\nCreate the core directory \"**biz**\"\n\n```sh\nmkdir biz\n```\n\n### 1. Create a biz file\n\nCreate file \"./biz/say/hi.js\". It defines an api \"/say/hi\" and handles it. \n\n```js\nmodule.exports = async (name, age) => {\n    return {msg: `Hi, I am ${name}, ${age} years old.`};\n};\n```\n\n### 2. Create index.js\n\n```js\nrequire('noapi')();\n```\n\n### 3. Run\n\n```sh\nnode index.js\nServer default is listening on port 3000\n```\n\nVisit the url [http://localhost:3000/say/hi?age=100&name=owen]() to see the result:\n\n```json\n{\n    \"success\": true,\n    \"data\": {\n        \"msg\": \"Hi, I am owen, 100 years old.\"\n    }\n}\n```\n\nThe order of the parameters in the url can be arbitrary.\n\n## Examples\n\n* [01 hello world](./examples/01-hello-world)\n* [02 complex url params](./examples/02-complex-url-params)\n* [03 returning an error](./examples/03-returning-an-error)\n* [04 throwing an error](./examples/04-throwing-an-error)\n* [05 api directory](./examples/05-api-directory)\n* [06 with database](./examples/06-with-database)\n* [07 static resources](./examples/07-static-resources)\n* [99 options](./examples/99-options)\n\n## Options\n\nIt can be omitted if it is the default value as below. \n\n```js\nconst name = 'default';\nconst dir = './biz';\nconst host = 'localhost';\nconst port = 3000; \nconst isSilence = false;\n\n// The number and order of parameters can be arbitrary\nnoapi(name, dir, host, port, isSilence);\n\n// It is equivalents to:\n//     const options = {\n//         name: 'default',\n//         dir: './biz',\n//         host: 'localhost',\n//         port: 3000,\n//         isSilence: true,\n//     };\n//\n//     noapi(options);\n```\n\nSee \"[examples/99-options](./examples/99-options)\" to learn about it.\n\n## Biz directory\n\nEach file in the biz directory defines and handles an API. All files in the biz directory make up the API list.\n\n```js\n/root\n    /biz\n        /say\n            hi.js       // api: /say/hi\n        about.js        // api: /about\n     \n    index.js    \n```\n\nIf there are some non-api files (only be used internally) in the biz directory, this will make the API list unclear. Then you should use the **api directory**, just create an empty file (or with the description of this api) to define an api. See \"[examples/05-api-directory](./examples/05-api-directory)\".\n\n```js\n/root\n    /api\n        /say\n            hi.js       // api: /say/hi\n        about.js        // api: /about\n\n    /biz\n        /__lib\n        /say\n            /__lib\n            hi.js   \n            tools.js    // non-api\n            check.js    // non-api\n        about.js       \n        init.js         // non-api\n        \n    index.js\n```\n\n## Performance\n\nNoapi is a high-performance web API framework, faster than [Koa](https://github.com/koajs/koa), [Express](https://github.com/expressjs/express). See [API Framework Performance PK](https://github.com/hiowenluke/api-frameworks-performance-pk)\n\n## Test\n\n```sh\ngit clone https://github.com/hiowenluke/noapi\ncd noapi\nnpm install\nnpm test\n```\n\n## License\n\n[MIT](LICENSE)\n\nCopyright (c) 2019, Owen Luke\n"
  },
  {
    "path": "examples/01-hello-world/biz/about.js",
    "content": "\n// Test\n// http://localhost:3000/about\n\n// Result\n//\t\t{\n// \t\t\t\"success\": true,\n// \t\t\t\"data\": {\n//\t\t\t\t\"version\": \"1.0.0\"\n// \t\t\t}\n// \t\t}\n\nconst fn = async () => {\n\treturn {version: '1.0.0'};\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/01-hello-world/biz/say/hi.js",
    "content": "\n// Test\n// http://localhost:3000/say/hi?age=100&name=owen\n\n// Result\n// \t\t{\n// \t\t\t\"success\": true,\n// \t\t\t\"data\": {\n// \t\t\t\t\"msg\": \"Hi, I am owen, 100 years old.\"\n// \t\t\t}\n// \t\t}\n\n// The order of the function parameters does NOT have to be\n// the same as the order of the url parameters.\nconst fn = async (name, age) => {\n\treturn {msg: `Hi, I am ${name}, ${age} years old.`};\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/01-hello-world/index.js",
    "content": "\nrequire('../noapi')();\n"
  },
  {
    "path": "examples/02-complex-url-params/biz/foo/bar.js",
    "content": "\n// Test\n// http://localhost:3000/foo/bar?name=owen&obj={\"date\":\"2019-05-01\"}&arr=[1,\"abc\",{\"tel\":12345678}]\n\n// Result\n// \t\t{\n// \t\t\t\"success\": true,\n// \t\t\t\"data\": {\n// \t\t\t\t\"name\": \"owen\",\n// \t\t\t\t\"obj\": {\n// \t\t\t\t\t\"date\": \"2019-05-01\"\n// \t\t\t\t},\n// \t\t\t\t\"arr\": [\n// \t\t\t\t\t1,\n// \t\t\t\t\t\"abc\",\n// \t\t\t\t\t{\n// \t\t\t\t\t\t\"tel\": 12345678\n// \t\t\t\t\t}\n// \t\t\t\t]\n// \t\t\t}\n// \t\t}\n\nconst fn = async (name, obj, arr) => {\n\treturn {name, obj, arr};\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/02-complex-url-params/index.js",
    "content": "\nrequire('../noapi')();\n"
  },
  {
    "path": "examples/03-returning-an-error/biz/do/somethingIsWrong.js",
    "content": "\n// Test\n// http://localhost:3000/do/somethingIsWrong\n\n// Result\n// \t\t{\n// \t\t\t\"success\": false,\n// \t\t\t\"error\": \"something is wrong\"\n// \t\t}\n\nconst fn = async () => {\n\treturn {error: 'something is wrong'};\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/03-returning-an-error/index.js",
    "content": "\nrequire('../noapi')();\n"
  },
  {
    "path": "examples/04-throwing-an-error/biz/do/say/hi.js",
    "content": "\n// Test\n// http://localhost:3000/do/say/hi\n\nconst fn = async () => {\n\tthrow new Error('something is wrong');\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/04-throwing-an-error/index.js",
    "content": "\nrequire('../noapi')();\n"
  },
  {
    "path": "examples/05-api-directory/api/about.js",
    "content": "\n// This file can be empty, or with the description of this api.\n// See ./say/hi.js\n"
  },
  {
    "path": "examples/05-api-directory/api/say/hi.js",
    "content": "/**\n * @test\n \thttp://localhost:3000/say/hi?age=100&name=owen\n\n * @returns\n\t{\n\t\t\"success\": true,\n\t\t\"data\": {\n\t\t\t\"msg\": \"Hi, I am owen, 100 years old.\",\n\t\t\t\"reversed\": \".dlo sraey 001 ,newo ma I ,iH\",\n\t\t\t\"encoded\": \"SGksIEkgYW0gb3dlbiwgMTAwIHllYXJzIG9sZC4=\"\n\t\t}\n\t}\n\n * @param {String} \tname \tuser's name with a maximum length of 32\n * @param {Integer} age\t\tuser's age with a maximum of 200\n * */\n"
  },
  {
    "path": "examples/05-api-directory/biz/__lib/reverse.js",
    "content": "\nconst fn = (str) => {\n\treturn str.split(\"\").reverse().join(\"\");\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/05-api-directory/biz/about.js",
    "content": "\nconst fn = async () => {\n\treturn {version: '1.0.0'};\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/05-api-directory/biz/say/__lib/decode.js",
    "content": "\nconst fn = (b64Encoded) => {\n\treturn Buffer.from(b64Encoded, 'base64').toString();\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/05-api-directory/biz/say/__lib/encode.js",
    "content": "\nconst fn = (str) => {\n\treturn Buffer.from(str).toString('base64');\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/05-api-directory/biz/say/hi.js",
    "content": "\nconst reverse = require('../__lib/reverse');\nconst encode = require('./__lib/encode');\n\nconst fn = async (name, age) => {\n\tconst msg = `Hi, I am ${name}, ${age} years old.`;\n\tconst reversed = reverse(msg);\n\tconst encoded = encode(msg);\n\n\treturn {msg, reversed, encoded};\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/05-api-directory/index.js",
    "content": "\nrequire('../noapi')();\n"
  },
  {
    "path": "examples/06-with-database/biz/about.js",
    "content": "\n// Test\n// http://localhost:3000/about\n\n// Result\n//\t\t{\n// \t\t\t\"success\": true,\n// \t\t\t\"data\": {\n//\t\t\t\t\"version\": \"1.0.0\"\n// \t\t\t}\n// \t\t}\n\nconst fn = async () => {\n\treturn {version: '1.0.0'};\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/06-with-database/biz/user/get.js",
    "content": "\n// Test url\n// http://localhost:3000/user/get?username=owen\n\nconst db = require('../../db');\n\nconst fn = async (username) => {\n\treturn await db.user.select(username);\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/06-with-database/biz/user/kill.js",
    "content": "\n// Test url\n// http://localhost:3000/user/kill?username=owen\n\nconst db = require('../../db');\n\nconst fn = async (username) => {\n\treturn await db.user.delete(username);\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/06-with-database/biz/user/list.js",
    "content": "\n// Test url\n// http://localhost:3000/user/list\n\nconst db = require('../../db');\n\nconst fn = async () => {\n\treturn await db.user.select();\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/06-with-database/biz/user/login.js",
    "content": "\n// Test url\n// http://localhost:3000/user/login?username=owen&password=123\n\nconst db = require('../../db');\n\nconst fn = async (username, password) => {\n\tconst result = await db.user.update({username, password}, {isOnline: 1});\n\treturn !!result;\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/06-with-database/biz/user/logout.js",
    "content": "\n// Test url\n// http://localhost:3000/user/logout?username=owen\n\nconst db = require('../../db');\n\nconst fn = async (username) => {\n\tconst result = await db.user.update(username, {isOnline: 0});\n\treturn !!result;\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/06-with-database/biz/user/register.js",
    "content": "\n// Test url\n// http://localhost:3000/user/register?username=owen&password=123\n\nconst db = require('../../db');\n\nconst fn = async (username, password) => {\n\treturn await db.user.insert({username, password});\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/06-with-database/db/index.js",
    "content": "\nconst data = {\n\tuser: [\n\t\t{\n\t\t\tid: 1,\n\t\t\tusername: 'admin',\n\t\t\tpassword: '123456',\n\t\t\tisOnline: 0,\n\t\t}\n\t]\n};\n\n// Fake database\nconst db = {\n\tuser: {\n\t\tasync insert(userInfo) {\n\t\t\tconst id = data.user.length + 1;\n\n\t\t\tuserInfo.isOnline = 0;\n\t\t\tuserInfo.id = id;\n\t\t\tdata.user.push(userInfo);\n\n\t\t\treturn id;\n\t\t},\n\n\t\tasync update(where, filedValues) {\n\t\t\tconst userInfo = await this.select(where);\n\t\t\tif (!userInfo) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tconst keys = Object.keys(filedValues);\n\t\t\tkeys.forEach(key => {\n\t\t\t\tuserInfo[key] = filedValues[key];\n\t\t\t});\n\n\t\t\treturn 1;\n\t\t},\n\n\t\tasync delete(where) {\n\t\t\tconst userInfoId = await this.select(where, true);\n\t\t\tif (userInfoId) {\n\t\t\t\tdata.user.splice(userInfoId - 1, 1);\n\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\treturn 0;\n\t\t},\n\n\t\tasync select(where, isReturnId) {\n\t\t\tif (!where) {\n\t\t\t\treturn data.user;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (typeof where === 'string') {\n\t\t\t\t\twhere = {username: where};\n\t\t\t\t}\n\n\t\t\t\tconst whereKeys = Object.keys(where);\n\t\t\t\tconst userInfo = data.user.find(item => {\n\t\t\t\t\tconst index = whereKeys.findIndex(key => where[key] !== item[key]);\n\t\t\t\t\tif (index === -1) {\n\t\t\t\t\t\treturn item;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif (userInfo) {\n\t\t\t\t\treturn isReturnId ? userInfo.id : userInfo;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n\nmodule.exports = db;\n"
  },
  {
    "path": "examples/06-with-database/index.js",
    "content": "\nrequire('../noapi')();\n"
  },
  {
    "path": "examples/07-static-resources/biz/about.js",
    "content": "\n// Test\n// http://localhost:3000/about\n\n// Result\n//\t\t{\n// \t\t\t\"success\": true,\n// \t\t\t\"data\": {\n//\t\t\t\t\"version\": \"1.0.0\"\n// \t\t\t}\n// \t\t}\n\nconst fn = async () => {\n\treturn {version: '1.0.0'};\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/07-static-resources/index.js",
    "content": "\nrequire('../noapi')();\n"
  },
  {
    "path": "examples/07-static-resources/public/images/1.jsx",
    "content": "// File type .jsx is not supported\n"
  },
  {
    "path": "examples/07-static-resources/public/index.html",
    "content": "Hello, world!\n"
  },
  {
    "path": "examples/07-static-resources/public/nav/index.html",
    "content": "/nav/index.html\n"
  },
  {
    "path": "examples/07-static-resources/public/nav/main.html",
    "content": "/nav/main.html\n"
  },
  {
    "path": "examples/99-options/config.js",
    "content": "\nmodule.exports = {\n\tname: 'myApi',\n\tdir: './src',\n\thost: '127.0.0.1',\n\tport: 3001,\n\tisSilence: true,\n};\n"
  },
  {
    "path": "examples/99-options/index.js",
    "content": "\nconst noapi = require('../noapi');\nconst config = require('./config');\n\nnoapi(config);\n"
  },
  {
    "path": "examples/99-options/src/about.js",
    "content": "\n// Test\n// http://localhost:3001/about\n\n// Result\n//\t\t{\n// \t\t\t\"success\": true,\n// \t\t\t\"data\": {\n//\t\t\t\t\"version\": \"1.0.0\"\n// \t\t\t}\n// \t\t}\n\nconst fn = async () => {\n\treturn {version: '1.0.0'};\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "examples/noapi.js",
    "content": "\nmodule.exports = require('..');\n"
  },
  {
    "path": "index.js",
    "content": "\nmodule.exports = require('./src');\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"noapi\",\n  \"version\": \"2.2.2\",\n  \"description\": \"A high-performance and easy-to-use web API framework for Node.js. Noapi loads directory \\\"biz\\\" as a web API server, each file in it defines and handles an API, so that you can focus on writing business function code. Noapi is simple enough that you just take it \\\"out of the box\\\".\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"node test\"\n  },\n  \"keywords\": [\n    \"node.js\",\n    \"api\",\n    \"microservices\",\n    \"microservices framework\"\n  ],\n  \"author\": \"hi.owen.luke@gmail.com\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"caller\": \"^1.0.1\",\n    \"kdo\": \"^1.8.1\",\n    \"keypaths\": \"^0.1.4\",\n    \"lodash\": \"^4.17.15\",\n    \"qs\": \"^6.9.1\"\n  },\n  \"devDependencies\": {\n    \"testor\": \"^0.6.5\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/hiowenluke/noapi.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/hiowenluke/noapi/issues\"\n  },\n  \"homepage\": \"https://github.com/hiowenluke/noapi#readme\"\n}\n"
  },
  {
    "path": "src/biz/do.js",
    "content": "\nconst paramsCache = require('./paramsCache');\nconst data = require('../data');\nconst keyPaths = require('keypaths');\n\nconst fn = async (api, query) => {\n\tconst apiX = data.apisX[api]; // \"/say/hi\" => \"say.hi\"\n\tconst func = keyPaths.get(data.handlers, apiX); // say.hi()\n\n\tif (typeof func !== 'function') {\n\t\treturn {error: `The handler ./biz${api}.js does not exists.`};\n\t}\n\n\tconst names = paramsCache.getByApi(api);\n\tconst args = names.map(name => query[name]);\n\n\tconst result = await func(...args);\n\treturn result;\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "src/biz/index.js",
    "content": "\nconst paramsCache = require('./paramsCache');\n\nconst me = {\n\tinit() {\n\t\tparamsCache.init();\n\t}\n};\n\nmodule.exports = me;\n"
  },
  {
    "path": "src/biz/paramsCache.js",
    "content": "\nconst fs = require('fs');\nconst data = require('../data');\nconst config = require('../config');\n\nconst parseParamsNames = (str) => { // \"(a, b = '', c) => {}\"\n\tconst temp = str.match(/\\((.*?)\\)/); // \"a, b = '', c\"\n\tif (!temp) return null;\n\n\tconst params = temp[1].split(','); // ['a', \" b = ''\", ' c']\n\tconst names = params.map(item => item.replace(/(^\\s*?(?=\\b))|(\\s*?$)/g, '').split('=')[0]); // ['a', 'b', 'c']\n\n\treturn names;\n};\n\nconst me = {\n\tdata: {},\n\n\tinit() {\n\t\tconst root = config.webServiceRoot;\n\t\tconst bizDir = config.dir.replace(/^./, ''); // ./biz => /biz\n\t\tconst apis = data.apis;\n\n\t\tapis.forEach(api => {\n\t\t\tlet filePath = root + bizDir + api;\n\n\t\t\t// The filePath will be exists if it is a directory\n\t\t\tif (!fs.existsSync(filePath)) {\n\n\t\t\t\t// If it it not exists, then check for .js file\n\t\t\t\tfilePath += '.js';\n\n\t\t\t\t// If not found, then ignore it\n\t\t\t\tif (!fs.existsSync(filePath)) return;\n\t\t\t}\n\n\t\t\t// Require the directory or js file\n\t\t\tconst bizFile = require(filePath);\n\t\t\tconst content = bizFile.toString(); // \"(a, b = '', c) => {}\"\n\n\t\t\tconst names = parseParamsNames(content);\n\t\t\tthis.data[api] = names;\n\t\t});\n\t},\n\n\tgetByApi(api) {\n\t\treturn this.data[api] || [];\n\t}\n};\n\nmodule.exports = me;\n"
  },
  {
    "path": "src/config.js",
    "content": "\nconst fs = require('fs');\nconst path = require('path');\n\nconst me = {\n\tname: 'default',\n\tdir: './biz',\n\thost: 'localhost',\n\tport: 3000,\n\tisSilence: false, // Do not print logs if it is true\n\n\t// 0 print \"Internal Server Error\"\n\t// 1 print error message and stack 1\n\t// 2 print full error stack\n\tonerror: 1,\n\n\twebServiceRoot: '', // The root path of web service\n\tenablePublic: '', // Enable public if the directory \"public\" is exists\n\n\tinit(pathToCaller, args = []) {\n\t\tthis.webServiceRoot = path.resolve(pathToCaller, '..');\n\t\tthis.enablePublic = fs.existsSync(this.webServiceRoot + '/public');\n\n\t\tlet name, dir, host, port, isSilence;\n\n\t\tif (typeof args[0] === 'object') {\n\t\t\t({name, dir, host, port, isSilence} = args[0]);\n\t\t}\n\t\telse {\n\t\t\targs.forEach(arg => {\n\t\t\t\tif (!arg) return;\n\n\t\t\t\tconst type = typeof arg;\n\t\t\t\tif (type === 'number') {\n\t\t\t\t\tport = arg;\n\t\t\t\t}\n\t\t\t\telse if (type === 'boolean') {\n\t\t\t\t\tisSilence = arg;\n\t\t\t\t}\n\t\t\t\telse if (type === 'string') {\n\t\t\t\t\tif (arg.substr(0, 1) === '.') {\n\t\t\t\t\t\tdir = arg;\n\t\t\t\t\t}\n\t\t\t\t\telse if (arg !== 'localhost' && arg.indexOf('.') === -1) {\n\t\t\t\t\t\tname = arg;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\thost = arg;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tname && (this.name = name);\n\t\tdir && (this.dir = dir);\n\t\thost && (this.host = host);\n\t\tport && (this.port = port);\n\t\tisSilence && (this.isSilence = isSilence);\n\t},\n};\n\nmodule.exports = me;\n"
  },
  {
    "path": "src/data.js",
    "content": "\nconst fs = require('fs');\nconst path = require('path');\nconst kdo = require('kdo');\n\nconst config = require('./config');\nconst keyPaths = require('keypaths');\n\nconst loadFolder = (folderName) => {\n\tlet obj;\n\n\tconst root = config.webServiceRoot;\n\tconst folderPath = path.resolve(root + '/' + folderName);\n\n\tif (fs.existsSync(folderPath)) {\n\t\tconst indexJs = folderPath + '/index.js';\n\n\t\tif (fs.existsSync(indexJs)) {\n\t\t\tobj = require(folderPath);\n\t\t}\n\t\telse {\n\t\t\tconst simulateIndexJs = {filename: indexJs};\n\t\t\tobj = kdo(simulateIndexJs);\n\t\t}\n\t}\n\n\treturn obj;\n};\n\nconst loadFolders = () => {\n\tconst core = {};\n\tconst bizDirName = config.dir.replace(/^.\\//, ''); // ./biz => biz\n\n\tcore.api = loadFolder('api');\n\tcore.biz = loadFolder(bizDirName);\n\n\tconst obj = core.api || core.biz || {};\n\tconst apiPaths = keyPaths.toPaths(obj); // [\"say.hi\"]\n\n\t// \"say.hi\" => \"/say/hi\"\n\tconst apis = apiPaths.map(item => '/' + item.replace(/\\./g, '/'));\n\n\tconst apisX = {};\n\n\t// {\"/say/hi\": \"say.hi\"}\n\tapis.forEach((api, index) => {apisX[api] = apiPaths[index]});\n\n\treturn {apis, apisX, handlers: core.biz};\n};\n\nconst me = {\n\tapis: [], // [\"/say/hi\"]\n\taha: {}, // {\"/say/hi\": \"say.hi\"}\n\thandlers: {}, // {say: {hi: f()}}\n\n\tinit() {\n\t\tconst {apis, apisX, handlers} = loadFolders();\n\n\t\tthis.apis = apis;\n\t\tthis.apisX = apisX;\n\t\tthis.handlers = handlers;\n\t},\n};\n\nmodule.exports = me;\n"
  },
  {
    "path": "src/index.js",
    "content": "\nconst caller = require('caller');\nconst config = require('./config');\n\nconst data = require('./data');\nconst biz = require('./biz');\nconst server = require('./server');\n\nconst fn = (...args) => {\n\tconfig.init(caller(), args);\n\n\tdata.init();\n\tbiz.init();\n\n\tserver.start();\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "src/server/cross.js",
    "content": "\nconst fn = (res) => {\n\n\t// Website you wish to allow to connect\n\tres.setHeader('Access-Control-Allow-Origin', '*');\n\n\t// Request methods you wish to allow\n\tres.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');\n\n\t// Request headers you wish to allow\n\tres.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');\n\n\t// Set to true if you need the website to include cookies in the requests sent\n\t// to the API (e.g. in case you use sessions)\n\tres.setHeader('Access-Control-Allow-Credentials', true);\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "src/server/index.js",
    "content": "\nconst http = require('http');\n\nconst config = require('../config');\nconst routes = require('./routes');\nconst processPublic = require('./public');\n\nconst done = (res, result) => {\n\tif (typeof result === 'undefined') {\n\t\tresult = {success: false, error: \"Internal Server Error\"};\n\t}\n\telse if (result && result.error) {\n\t\tresult = {success: false, error: result.error};\n\t}\n\telse {\n\t\tresult = {success: true, data: result};\n\t}\n\n\twrite(res, 'json', JSON.stringify(result));\n};\n\nconst write = (res, type, str) => {\n\tconst types = {\n\t\tjson: 'application/json',\n\t\thtml: 'text/html',\n\t};\n\n\tif (type === 'html' && !/^<html>/i.test(str)) {\n\t\tstr = `<html><body>${str}</body></html>`;\n\t}\n\n\tres.writeHead(200, {'Content-Type': types[type], 'X-Powered-By': 'Noapi'});\n\tres.write(str);\n\tres.end();\n};\n\nconst getQueryStr = (req) => {\n\treturn new Promise(resolve => {\n\t\tlet str = '';\n\n\t\treq.on('data', (chunk) => {\n\t\t\tstr += chunk;\n\t\t});\n\n\t\treq.on('end',()=>{\n\t\t\tresolve(str);\n\t\t});\n\t})\n};\n\nconst getErrorMessages = (arr) => {\n\tconst messages = [];\n\n\tarr.forEach(a => {\n\t\tif (a.substr(0, 7 ) !== '    at ') {\n\t\t\tmessages.push(a);\n\t\t}\n\t});\n\n\treturn messages;\n};\n\nconst me = {\n\tstart() {\n\t\tconst server = new http.Server();\n\t\tserver.on('request',async (req, res) => {\n\t\t\tconst url = req.url;\n\t\t\tlet [api, queryStr] = url.split('?');\n\n\t\t\tif (config.enablePublic) {\n\t\t\t\tconst done = processPublic(api, res);\n\t\t\t\tif (done) return;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (api === '/favicon.ico') {\n\t\t\t\t\treturn done(res, null);\n\t\t\t\t}\n\n\t\t\t\tif (api === '/') {\n\t\t\t\t\treturn done(res, 'Welcome to Noapi.');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst method = req.method.toLowerCase();\n\t\t\tif (method === 'post') {\n\t\t\t\tqueryStr = await getQueryStr(req);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tlet result = await routes(api, queryStr);\n\n\t\t\t\tif (result && result.error) {\n\t\t\t\t\t!config.isSilence && console.log(result.error);\n\t\t\t\t}\n\n\t\t\t\tdone(res, result);\n\t\t\t}\n\t\t\tcatch(e) {\n\t\t\t\tconst onerror = config.onerror;\n\t\t\t\t!config.isSilence && console.log(e);\n\n\t\t\t\tif (onerror === 0) {\n\t\t\t\t\tdone(res);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tconst arr = e.stack.split('\\n');\n\n\t\t\t\t\t// print error message with stack 1\n\t\t\t\t\tif (onerror === 1) {\n\t\t\t\t\t\tconst messages = getErrorMessages(arr);\n\t\t\t\t\t\tconst message = messages.join('\\n');\n\t\t\t\t\t\tlet script = arr.find(a => a.substr(0, 7) === '    at ');\n\t\t\t\t\t\tscript = script\n\t\t\t\t\t\t\t.replace('    at fn (', '')\n\t\t\t\t\t\t\t.replace(config.webServiceRoot, '.')\n\t\t\t\t\t\t\t.replace(/\\)$/, '')\n\t\t\t\t\t\t;\n\t\t\t\t\t\tdone(res, {error: {message, script}});\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// print full error stack\n\t\t\t\t\t\tlet str = arr.join('<br/>');\n\t\t\t\t\t\tstr = str.replace(/ {4}/g, '&nbsp;&nbsp;&nbsp;&nbsp;');\n\t\t\t\t\t\twrite(res, 'html', str);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tconst {name, host, port, isSilence} = config;\n\t\tserver.listen(port, () => {\n\t\t\tif (isSilence) return;\n\t\t\tconsole.log(`Server ${name} running at http://${host}:${port}`);\n\t\t});\n\t}\n};\n\nmodule.exports = me;\n"
  },
  {
    "path": "src/server/public/index.js",
    "content": "\nconst fs = require('fs');\nconst path = require('path');\n\nconst config = require('../../config');\nconst mime = require('./mime');\n\nfunction readFile(filePath, contentType, res) {\n\tres.writeHead(200, { 'content-type': contentType });\n\n\tconst stream = fs.createReadStream(filePath);\n\tstream.on('error', () => {\n\t\tres.writeHead(500, { 'content-type': contentType });\n\t\tres.end('<h1>500 Server Error</h1>')\n\t});\n\n\tstream.pipe(res);\n}\n\nconst response = (res, html) => {\n\tres.writeHead(200, { 'content-type': 'text/html' });\n\tres.end(html);\n};\n\nconst getFilePath = (pathName) => {\n\treturn path.resolve(config.webServiceRoot + '/public' + pathName);\n};\n\nconst fn = (pathName, res) => {\n\tif (pathName === '/') {\n\t\tpathName = '/index.html';\n\t}\n\n\tlet ext = path.extname(pathName);\n\text = ext ? ext.slice(1) : '';\n\n\t// non-static resources\n\tconst contentType = mime[ext];\n\tif (!contentType) {\n\n\t\tconst filePath = getFilePath(pathName);\n\t\tif (fs.existsSync(filePath)) {\n\n\t\t\t// It is a directory\n\t\t\tif (fs.statSync(filePath).isDirectory()) {\n\t\t\t\tconst index = filePath + '/index.html';\n\n\t\t\t\t// no index.html\n\t\t\t\tif (!fs.existsSync(index)) {\n\t\t\t\t\tresponse(res, 'Directory listing is not supported');\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// load index.html\n\t\t\t\t\treadFile(index, mime['html'], res);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// It is a file\n\t\t\t\t// That means its file type is not be supported\n\t\t\t\tresponse(res, 'File type .' + ext + ' is not supported');\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\t// It is an api, then do nothing\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tconst filePath = getFilePath(pathName);\n\tif (!fs.existsSync(filePath)) {\n\t\t// not found\n\t\tresponse(res, pathName + ' is not found');\n\t}\n\telse {\n\t\t// static resources\n\t\treadFile(filePath, contentType, res);\n\t}\n\n\treturn true;\n};\n\nmodule.exports = fn;\n\n"
  },
  {
    "path": "src/server/public/mime.js",
    "content": "\nconst me = {\n\tcss: 'text/css',\n\tgif: 'image/gif',\n\thtml: 'text/html',\n\tico: 'image/x-icon',\n\tjpeg: 'image/jpeg',\n\tjpg: 'image/jpeg',\n\tjs: 'text/javascript',\n\tjson: 'application/json',\n\tpdf: 'application/pdf',\n\tpng: 'image/png',\n\tsvg: 'image/svg+xml',\n\tswf: 'application/x-shockwave-flash',\n\ttiff: 'image/tiff',\n\ttxt: 'text/plain',\n\twav: 'audio/x-wav',\n\twma: 'audio/x-ms-wma',\n\twmv: 'video/x-ms-wmv',\n\txml: 'text/xml'\n};\n\nmodule.exports = me;\n"
  },
  {
    "path": "src/server/routes/index.js",
    "content": "\nconst config = require('../../config');\nconst data = require('../../data');\nconst bizDo = require('../../biz/do');\nconst parseQueryStr = require('./parseQueryStr');\n\nconst fn = async (api, queryStr) => {\n\n\tconst isApiExists = data.apis.indexOf(api) >= 0;\n\tif (!isApiExists) {\n\t\treturn {error: `${api} is not found`};\n\t}\n\n\tconst query = parseQueryStr.do(queryStr);\n\tif (query && query.error) {\n\t\treturn query;\n\t}\n\n\tconst result = await bizDo(api, query);\n\treturn result;\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "src/server/routes/parseQueryStr.js",
    "content": "\nconst url  = require('url');\nconst qs = require('qs');\n\nconst parseQueryStr = (str) => {\n\tlet query;\n\tlet errArg;\n\tlet isComplexQueryStr;\n\n\ttry {\n\t\t// From post\n\t\t// name=owen&obj%5Bdate%5D=2019-05-01&arr%5B0%5D=1&arr%5B1%5D=abc&arr%5B2%5D%5Btel%5D=12345678\n\t\tif (/(^|&).+?\\b((%5B)|(\\[))/.test(str)) {\n\t\t\tisComplexQueryStr = 1;\n\t\t\tquery = qs.parse(str);\n\t\t}\n\t\telse {\n\t\t\t// From get\n\t\t\t// name=owen&obj={%22date%22:%222019-05-01%22}&arr=[1,%22abc%22,{%22tel%22:12345678}]\n\n\t\t\t// Simulate a complete url-string so that we can use url.parse() to parse it\n\t\t\tquery = url.parse('/?' + str, true).query;\n\n\t\t\tconst keys = Object.keys(query);\n\t\t\tkeys.forEach(key => {\n\t\t\t\tconst val = query[key];\n\t\t\t\tif (!/[[{]/.test(val)) return;\n\n\t\t\t\terrArg = key;\n\n\t\t\t\t// Processes only standard json strings, regardless of object.\n\t\t\t\tquery[key] = JSON.parse(val);\n\t\t\t});\n\t\t}\n\t}\n\tcatch(e) {\n\t\tconsole.log(e);\n\n\t\tif (isComplexQueryStr) {\n\t\t\tquery = {error: `${str} has invalid json string.`}\n\t\t}\n\t\telse {\n\t\t\tquery = {error: `${errArg} is an invalid json string.`}\n\t\t}\n\t}\n\n\treturn query;\n};\n\n\nconst me = {\n\tcache: {},\n\n\tdo(queryStr) {\n\t\tif (!queryStr) {\n\t\t\treturn {};\n\t\t}\n\n\t\tif (!this.cache[queryStr]) {\n\t\t\tconst query = parseQueryStr(queryStr);\n\t\t\tif (query && query.error) {\n\t\t\t\treturn query;\n\t\t\t}\n\n\t\t\t// ECMAScript 2016 (ed. 7) established a maximum length of 2^53 - 1 elements\n\t\t\t// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length\n\t\t\tthis.cache[queryStr] = query;\n\t\t}\n\n\t\treturn this.cache[queryStr];\n\t},\n};\n\nmodule.exports = me;\n"
  },
  {
    "path": "test/forExamples/index.js",
    "content": "\nconst fs = require('fs');\nconst path = require('path');\nconst cp = require('child_process');\n\nconst main = () => {\n\tconst testorPath = path.resolve(__dirname, '../../node_modules/testor/bin/testor');\n\tconst examplesPath = path.resolve(__dirname, '../../examples');\n\tconst testCasesPath = path.resolve(__dirname, './testCases');\n\n\tconst exampleNames = fs.readdirSync(examplesPath);\n\texampleNames.forEach(exampleName => {\n\t\tif (exampleName.substr(0, 1) === '.') return;\n\n\t\tconst examplePath = examplesPath + '/' + exampleName;\n\t\tif (!fs.statSync(examplePath).isDirectory()) return;\n\n\t\tconst exampleTestPath = examplePath + '/test';\n\t\tconst targetCasesFile = exampleTestPath + '/cases.js';\n\n\t\tfs.mkdirSync(exampleTestPath);\n\t\tfs.copyFileSync(testCasesPath + '/' + exampleName + '.js', targetCasesFile);\n\n\t\tconst args = [testorPath, examplePath];\n\t\tconst userConfigFile = fs.existsSync(examplePath + '/config.js') ? '--config' : '';\n\t\tif (userConfigFile) args.push(userConfigFile);\n\n\t\tcp.spawnSync('node', args, {stdio: \"inherit\"});\n\n\t\tfs.unlinkSync(exampleTestPath + '/cases.js');\n\t\tfs.rmdirSync(exampleTestPath);\n\t});\n};\n\nmain();\n"
  },
  {
    "path": "test/forExamples/testCases/01-hello-world.js",
    "content": "\nconst me = [\n\n\t'/say/hi',\n\t{\n\t\tparams: {\n\t\t\tname: 'owen',\n\t\t\tage: 100\n\t\t},\n\n\t\tresult: {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\tmsg: \"Hi, I am owen, 100 years old.\"\n\t\t\t}\n\t\t}\n\t},\n\n\t'/about',\n\t{\n\t\tresult: {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\t\"version\": \"1.0.0\"\n\t\t\t}\n\t\t}\n\t},\n];\n\nmodule.exports = me;\n"
  },
  {
    "path": "test/forExamples/testCases/02-complex-url-params.js",
    "content": "\nconst me = [\n\n\t'/foo/bar',\n\t{\n\t\tparams: {\n\t\t\tname: \"owen\",\n\t\t\tobj: {\n\t\t\t\tdate: \"2019-05-01\"\n\t\t\t},\n\t\t\tarr: [\n\t\t\t\t1,\n\t\t\t\t\"abc\",\n\t\t\t\t{\n\t\t\t\t\ttel: 12345678\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\n\t\tresult: {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\t\"name\": \"owen\",\n\t\t\t\t\"obj\": {\n\t\t\t\t\t\"date\": \"2019-05-01\"\n\t\t\t\t},\n\t\t\t\t\"arr\": [\n\t\t\t\t\t\"1\",\n\t\t\t\t\t\"abc\",\n\t\t\t\t\t{\n\t\t\t\t\t\t\"tel\": \"12345678\"\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t}\n\t},\n];\n\nmodule.exports = me;\n"
  },
  {
    "path": "test/forExamples/testCases/03-returning-an-error.js",
    "content": "\nconst me = [\n\n\t'/do/somethingIsWrong',\n\t{\n\t\tresult: {\n\t\t\tsuccess: false,\n\t\t\terror: \"something is wrong\"\n\t\t}\n\t},\n];\n\nmodule.exports = me;\n"
  },
  {
    "path": "test/forExamples/testCases/04-throwing-an-error.js",
    "content": "\nconst me = [\n\n\t'/do/say/hi',\n\t{\n\t\tverify(result) {\n\t\t\treturn result.error.message.indexOf('something is wrong') >= 0;\n\t\t}\n\t},\n];\n\nmodule.exports = me;\n"
  },
  {
    "path": "test/forExamples/testCases/05-api-directory.js",
    "content": "\nconst me = [\n\n\t'/say/hi',\n\t{\n\t\tparams: {\n\t\t\tname: 'owen',\n\t\t\tage: 100\n\t\t},\n\n\t\tresult: {\n\t\t\t\"success\": true,\n\t\t\t\"data\": {\n\t\t\t\t\"msg\": \"Hi, I am owen, 100 years old.\",\n\t\t\t\t\"reversed\": \".dlo sraey 001 ,newo ma I ,iH\",\n\t\t\t\t\"encoded\": \"SGksIEkgYW0gb3dlbiwgMTAwIHllYXJzIG9sZC4=\"\n\t\t\t}\n\t\t}\n\t},\n\n\t'/about',\n\t{\n\t\tresult: {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\t\"version\": \"1.0.0\"\n\t\t\t}\n\t\t}\n\t},\n];\n\nmodule.exports = me;\n"
  },
  {
    "path": "test/forExamples/testCases/06-with-database.js",
    "content": "\nconst me = [\n\n\t'/user/login?username=owen&password=123',\n\t{\n\t\tbefore: [\n\t\t\t'/user/register?username=owen&password=123',\n\t\t],\n\n\t\tafter: [\n\t\t\t'/user/kill?username=owen',\n\t\t],\n\n\t\tverify(result) {\n\t\t\treturn result.data === true;\n\t\t}\n\t},\n\n\t'/user/logout?username=owen',\n\t{\n\t\tbefore: [\n\t\t\t'/user/register?username=owen&password=123',\n\t\t\t'/user/login?username=owen&password=123',\n\t\t],\n\n\t\tresultUrl: '/user/get?username=owen',\n\n\t\tafter: [\n\t\t\t'/user/kill?username=owen',\n\t\t],\n\n\t\tverify(result) {\n\t\t\treturn result.data.isOnline === 0;\n\t\t}\n\t},\n];\n\nmodule.exports = me;\n"
  },
  {
    "path": "test/forExamples/testCases/07-static-resources.js",
    "content": "\nconst me = [\n\n\t'/',\n\t{\n\t\tverify(result) {\n\t\t\treturn result.indexOf('Hello, world!') >= 0;\n\t\t}\n\t},\n\n\t'/images',\n\t{\n\t\tverify(result) {\n\t\t\treturn result.indexOf('Directory listing is not supported') >= 0;\n\t\t}\n\t},\n\n\t'/images/1.jsx',\n\t{\n\t\tverify(result) {\n\t\t\treturn result.indexOf('File type .jsx is not supported') >= 0;\n\t\t}\n\t},\n\n\t'/images/guitar.png',\n\t{\n\t\tverify(result) {\n\t\t\treturn result.indexOf('\\u0000') >= 0;\n\t\t}\n\t},\n\n\t'/nav',\n\t{\n\t\tverify(result) {\n\t\t\treturn result.indexOf('/nav/index.html') >= 0;\n\t\t}\n\t},\n\n\t'/nav/main.html',\n\t{\n\t\tverify(result) {\n\t\t\treturn result.indexOf('/nav/main.html') >= 0;\n\t\t}\n\t},\n\n\t'/about',\n\t{\n\t\tsuccess: true,\n\t\tdata: {\n\t\t\t\"version\": \"1.0.0\"\n\t\t}\n\t},\n];\n\nmodule.exports = me;\n"
  },
  {
    "path": "test/forExamples/testCases/99-options.js",
    "content": "\nconst me = [\n\t'/about',\n\t{\n\t\tresult: {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\t\"version\": \"1.0.0\"\n\t\t\t}\n\t\t}\n\t},\n];\n\nmodule.exports = me;\n"
  },
  {
    "path": "test/index.js",
    "content": "\nrequire('./forExamples');\nrequire('./tests');\n"
  },
  {
    "path": "test/tests/api-folder/api/about.js",
    "content": "\n// Test\n// http://localhost:3000/about\n"
  },
  {
    "path": "test/tests/api-folder/index.js",
    "content": "\nconst noapi = require('../../..');\nnoapi();\n"
  },
  {
    "path": "test/tests/api-folder/test/cases.js",
    "content": "\nconst me = [\n\t'/about',\n\t{\n\t\tresult: {\n\t\t\t\"success\": false,\n\t\t\t\"error\": \"The handler ./biz/about.js does not exists.\"\n\t\t}\n\t},\n];\n\nmodule.exports = me;\n"
  },
  {
    "path": "test/tests/default/biz/get.js",
    "content": "\nconst fn = async (name, obj, arr) => {\n\treturn {name, obj, arr};\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "test/tests/default/biz/post.js",
    "content": "\nconst fn = async (name, obj, arr) => {\n\treturn {name, obj, arr};\n};\n\nmodule.exports = fn;\n"
  },
  {
    "path": "test/tests/default/index.js",
    "content": "\nconst noapi = require('../../..');\nnoapi();\n"
  },
  {
    "path": "test/tests/default/test/cases.js",
    "content": "\nconst me = [\n\t'/',\n\t{\n\t\tresult: {\n\t\t\t\"success\": true,\n\t\t\t\"data\": \"Welcome to Noapi.\"\n\t\t}\n\t},\n\n\t'/xxx',\n\t{\n\t\tresult: {\n\t\t\t\"success\": false,\n\t\t\t\"error\": \"/xxx is not found\"\n\t\t}\n\t},\n\n\t'/get',\n\t{\n\t\tmethod: 'GET',\n\n\t\tparams: {\n\t\t\t\"name\": \"owen\",\n\t\t\t\"obj\": {\n\t\t\t\t\"date\": \"2019-05-01\"\n\t\t\t},\n\t\t\t\"arr\": [\n\t\t\t\t1,\n\t\t\t\t\"abc\",\n\t\t\t\t{\n\t\t\t\t\t\"tel\": 12345678\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\n\t\tresult: {\n\t\t\t\"success\": true,\n\t\t\t\"data\": {\n\t\t\t\t\"name\": \"owen\",\n\t\t\t\t\"obj\": {\n\t\t\t\t\t\"date\": \"2019-05-01\"\n\t\t\t\t},\n\t\t\t\t\"arr\": [\n\t\t\t\t\t\"1\",\n\t\t\t\t\t\"abc\",\n\t\t\t\t\t{\n\t\t\t\t\t\t\"tel\": \"12345678\"\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t}\n\t},\n\n\t'/post',\n\t{\n\t\tmethod: 'POST',\n\n\t\tparams: {\n\t\t\t\"name\": \"owen\",\n\t\t\t\"obj\": {\n\t\t\t\t\"date\": \"2019-05-01\"\n\t\t\t},\n\t\t\t\"arr\": [\n\t\t\t\t1,\n\t\t\t\t\"abc\",\n\t\t\t\t{\n\t\t\t\t\t\"tel\": 12345678\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\n\t\tresult: {\n\t\t\t\"success\": true,\n\t\t\t\"data\": {\n\t\t\t\t\"name\": \"owen\",\n\t\t\t\t\"obj\": {\n\t\t\t\t\t\"date\": \"2019-05-01\"\n\t\t\t\t},\n\t\t\t\t\"arr\": [\n\t\t\t\t\t\"1\",\n\t\t\t\t\t\"abc\",\n\t\t\t\t\t{\n\t\t\t\t\t\t\"tel\": \"12345678\"\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t}\n\t}\n];\n\nmodule.exports = me;\n"
  },
  {
    "path": "test/tests/index.js",
    "content": "\nconst fs = require('fs');\nconst path = require('path');\nconst cp = require('child_process');\n\nconst main = () => {\n\tconst testorPath = path.resolve(__dirname, '../../node_modules/testor/bin/testor');\n\tconst examplesPath = __dirname;\n\tconst exampleNames = fs.readdirSync(examplesPath);\n\n\texampleNames.forEach(exampleName => {\n\t\tif (exampleName.substr(0, 1) === '.') return;\n\n\t\tconst examplePath = examplesPath + '/' + exampleName;\n\t\tif (!fs.statSync(examplePath).isDirectory()) return;\n\n\t\tcp.spawnSync('node', [testorPath, examplePath], {stdio: \"inherit\"});\n\t});\n};\n\nmain();\n"
  }
]